功能特性

本節更詳細地介紹了 Spring Cloud Task,包括如何使用它、如何配置它以及適當的擴充套件點。

Spring Cloud Task 的生命週期

在大多數情況下,現代雲環境圍繞著那些預期不會終止的程序而設計。如果它們確實終止了,通常會重新啟動。雖然大多數平臺都有某種方式執行一個終止時不會被重新啟動的程序,但其執行結果通常無法以可消費的方式維護。Spring Cloud Task 提供了在環境中執行短期程序並記錄結果的能力。這樣做允許圍繞短期程序構建微服務架構,並透過訊息整合任務,實現更長時間執行的服務。

雖然此功能在雲環境中很有用,但在傳統部署模型中也可能出現同樣的問題。當使用 cron 等排程器執行 Spring Boot 應用程式時,能夠在應用程式完成後監控其結果會很有用。

Spring Cloud Task 採用這樣一種方法:Spring Boot 應用程式可以有開始和結束,並且仍然是成功的。批處理應用程式就是一種如何實現預期會終止(且通常是短期)的程序並使其發揮作用的示例。

Spring Cloud Task 會記錄給定任務的生命週期事件。大多數長時間執行的程序(以大多數 Web 應用程式為代表)不儲存它們的生命週期事件。而 Spring Cloud Task 的核心任務則會儲存。

生命週期由一次任務執行組成。這是一次 Spring Boot 應用程式的物理執行,該應用程式被配置為一個任務(即,它具有 Spring Cloud Task 依賴項)。

在任務開始時,在任何 CommandLineRunnerApplicationRunner 實現執行之前,會在 TaskRepository 中建立一個記錄開始事件的條目。此事件透過 Spring Framework 觸發 SmartLifecycle#start 來觸發。這向系統表明所有 bean 都已準備就緒,並且在此之前會執行 Spring Boot 提供的任何 CommandLineRunnerApplicationRunner 實現。

任務的記錄僅在 ApplicationContext 成功引導時發生。如果上下文完全無法引導,則不記錄該任務的執行。

在 Spring Boot 的所有 *Runner#run 呼叫完成後,或者 ApplicationContext 失敗(由 ApplicationFailedEvent 指示)時,任務執行的結果會在倉庫中更新。

如果應用程式要求在任務完成時關閉 ApplicationContext(所有 *Runner#run 方法都已呼叫且任務倉庫已更新),則將屬性 spring.cloud.task.closecontextEnabled 設定為 true。

TaskExecution

儲存在 TaskRepository 中的資訊在 TaskExecution 類中建模,包含以下資訊

欄位 描述

executionid

任務執行的唯一 ID。

exitCode

ExitCodeExceptionMapper 實現生成的退出碼。如果沒有生成退出碼但丟擲了 ApplicationFailedEvent,則設定為 1。否則,假定為 0。

taskName

任務的名稱,由配置的 TaskNameResolver 決定。

startTime

任務啟動的時間,由 SmartLifecycle#start 呼叫指示。

endTime

任務完成的時間,由 ApplicationReadyEvent 指示。

exitMessage

退出時可用的任何資訊。這可以透過 TaskExecutionListener 程式設計設定。

errorMessage

如果異常是任務結束的原因(由 ApplicationFailedEvent 指示),則該異常的堆疊跟蹤會儲存在此處。

arguments

傳遞給可執行 boot 應用程式的字串命令列引數的 List

對映退出碼

當任務完成時,它會嘗試向作業系統返回一個退出碼。如果我們看看我們的原始示例,可以看到我們沒有控制應用程式的這一方面。因此,如果丟擲異常,JVM 返回的程式碼可能對你的除錯有用,也可能沒有用。

因此,Spring Boot 提供了一個介面 ExitCodeExceptionMapper,允許你將未捕獲的異常對映到退出碼。這樣做可以讓你在退出碼級別指示出了什麼問題。此外,透過以這種方式對映退出碼,Spring Cloud Task 會記錄返回的退出碼。

如果任務因 SIG-INT 或 SIG-TERM 而終止,退出碼為零,除非程式碼中另有指定。

任務執行時,退出碼在倉庫中儲存為 null。任務完成後,根據本節前面描述的指南儲存相應的退出碼。

配置

Spring Cloud Task 提供了開箱即用的配置,如 DefaultTaskConfigurerSimpleTaskConfiguration 類中定義的那樣。本節將介紹預設配置以及如何根據需要自定義 Spring Cloud Task。

DataSource

Spring Cloud Task 使用資料來源來儲存任務執行結果。預設情況下,我們提供了一個記憶體中的 H2 例項,以提供簡單的開發引導方法。然而,在生產環境中,你可能希望配置自己的 DataSource

如果你的應用程式只使用一個 DataSource,並且它既作為你的業務 schema 又作為任務倉庫,你只需提供任何 DataSource(最簡單的方法是透過 Spring Boot 的配置約定)。Spring Cloud Task 會自動將此 DataSource 用於倉庫。

如果你的應用程式使用多個 DataSource,你需要使用適當的 DataSource 配置任務倉庫。此自定義可以透過 TaskConfigurer 的實現來完成。

表字首

TaskRepository 的一個可修改屬性是任務表的表字首。預設情況下,它們都以 TASK_ 開頭。TASK_EXECUTIONTASK_EXECUTION_PARAMS 是兩個示例。然而,修改此字首可能有潛在的原因。如果 schema 名稱需要新增到表名稱前面,或者在同一個 schema 中需要多組任務表,則必須更改表字首。你可以透過將 spring.cloud.task.tablePrefix 設定為你需要的字首來完成此操作,如下所示

spring.cloud.task.tablePrefix=yourPrefix

透過使用 spring.cloud.task.tablePrefix,使用者承擔建立任務表的責任,這些任務表需要滿足任務表 schema 的標準,但要進行使用者業務所需的修改。在建立自己的任務 DDL 時,你可以利用 Spring Cloud Task Schema DDL 作為指南,此處提供了示例。

啟用/停用表初始化

在你自己建立任務表,並且不希望 Spring Cloud Task 在任務啟動時建立它們的情況下,請將 spring.cloud.task.initialize-enabled 屬性設定為 false,如下所示

spring.cloud.task.initialize-enabled=false

預設為 true

屬性 spring.cloud.task.initialize.enable 已棄用。

外部生成的任務 ID

在某些情況下,你可能希望考慮任務被請求與基礎設施實際啟動它之間的時間差。Spring Cloud Task 允許你在任務被請求時建立一個 TaskExecution。然後將生成的 TaskExecution 的執行 ID 傳遞給任務,以便它可以在任務的生命週期中更新 TaskExecution

可以透過在引用儲存 TaskExecution 物件的資料庫的 TaskRepository 實現上呼叫 createTaskExecution 方法來建立 TaskExecution

為了配置你的任務使用生成的 TaskExecutionId,新增以下屬性

spring.cloud.task.executionid=yourtaskId

外部任務 Id

Spring Cloud Task 允許你為每個 TaskExecution 儲存一個外部任務 ID。為了配置你的任務使用生成的 TaskExecutionId,新增以下屬性

spring.cloud.task.external-execution-id=<externalTaskId>

父任務 Id

Spring Cloud Task 允許你為每個 TaskExecution 儲存一個父任務 ID。一個例子是某個任務執行另一個或多個任務,並且你想要記錄是哪個任務啟動了每個子任務。為了配置你的任務設定父 TaskExecutionId,在子任務上新增以下屬性

spring.cloud.task.parent-execution-id=<parentExecutionTaskId>

TaskConfigurer

TaskConfigurer 是一個策略介面,允許你自定義 Spring Cloud Task 元件的配置方式。預設情況下,我們提供了 DefaultTaskConfigurer,它提供了邏輯預設值:基於 Map 的記憶體元件(如果沒有提供 DataSource,這對於開發很有用)和基於 JDBC 的元件(如果提供了 DataSource,這很有用)。

TaskConfigurer 允許你配置三個主要元件

元件 描述 預設值(由 DefaultTaskConfigurer 提供)

TaskRepository

要使用的 TaskRepository 實現。

SimpleTaskRepository

TaskExplorer

要使用的 TaskExplorer 實現(用於對任務倉庫進行只讀訪問的元件)。

SimpleTaskExplorer

PlatformTransactionManager

執行任務更新時使用的事務管理器。

如果使用 DataSource,則為 JdbcTransactionManager。如果未使用,則為 ResourcelessTransactionManager

你可以透過建立 TaskConfigurer 介面的自定義實現來定製前面表格中描述的任何元件。通常,繼承 DefaultTaskConfigurer(如果找不到 TaskConfigurer 則提供)並重寫所需的 getter 方法就足夠了。然而,可能需要從頭開始實現自己的元件。

使用者不應直接使用 TaskConfigurer 的 getter 方法,除非他們使用它來提供實現以暴露為 Spring Bean。

任務執行監聽器

TaskExecutionListener 允許你註冊任務生命週期中發生的特定事件的監聽器。為此,建立一個實現 TaskExecutionListener 介面的類。實現 TaskExecutionListener 介面的類會收到以下事件通知

  • onTaskStartup:在將 TaskExecution 儲存到 TaskRepository 之前。

  • onTaskEnd:在更新 TaskRepository 中的 TaskExecution 條目並標記任務的最終狀態之前。

  • onTaskFailed:在任務丟擲未處理異常時呼叫 onTaskEnd 方法之前。

Spring Cloud Task 還允許你使用以下方法註解將 TaskExecution 監聽器新增到 bean 中的方法

  • @BeforeTask:在將 TaskExecution 儲存到 TaskRepository 之前

  • @AfterTask:在更新 TaskExecution 中的 TaskExecution 條目並標記任務最終狀態之前。

  • @FailedTask:在任務丟擲未處理異常時呼叫 @AfterTask 方法之前。

以下示例顯示了這三個註解的使用

 public class MyBean {

	@BeforeTask
	public void methodA(TaskExecution taskExecution) {
	}

	@AfterTask
	public void methodB(TaskExecution taskExecution) {
	}

	@FailedTask
	public void methodC(TaskExecution taskExecution, Throwable throwable) {
	}
}
在鏈中早於 TaskLifecycleListener 插入 ApplicationListener 可能會導致意外的影響。

任務執行監聽器丟擲的異常

如果 TaskExecutionListener 事件處理程式丟擲異常,則該事件處理程式的所有監聽器處理都將停止。例如,如果三個 onTaskStartup 監聽器已經啟動,並且第一個 onTaskStartup 事件處理程式丟擲異常,則不會呼叫另外兩個 onTaskStartup 方法。然而,TaskExecutionListener 的其他事件處理程式(onTaskEndonTaskFailed)會被呼叫。

TaskExecutionListener 事件處理程式丟擲異常時返回的退出碼是 ExitCodeEvent 報告的退出碼。如果沒有發出 ExitCodeEvent,則會評估丟擲的異常,看它是否是 ExitCodeGenerator 型別。如果是,則返回來自 ExitCodeGenerator 的退出碼。否則,返回 1

如果在 onTaskStartup 方法中丟擲異常,應用程式的退出碼將為 1。如果在 onTaskEndonTaskFailed 方法中丟擲異常,應用程式的退出碼將是根據上述規則確定的值。

如果在 onTaskStartuponTaskEndonTaskFailed 中丟擲異常,你無法使用 ExitCodeExceptionMapper 覆蓋應用程式的退出碼。

退出訊息

你可以透過使用 TaskExecutionListener 以程式設計方式設定任務的退出訊息。這是透過設定 TaskExecutionexitMessage 來完成的,然後它會傳遞給 TaskExecutionListener。以下示例顯示了一個使用 @AfterTask ExecutionListener 註解的方法

@AfterTask
public void afterMe(TaskExecution taskExecution) {
    taskExecution.setExitMessage("AFTER EXIT MESSAGE");
}

可以在任何監聽器事件(onTaskStartuponTaskFailedonTaskEnd)中設定 ExitMessage。這三個監聽器的優先順序如下

  1. onTaskEnd

  2. onTaskFailed

  3. onTaskStartup

例如,如果你為 onTaskStartuponTaskFailed 監聽器設定了 exitMessage,並且任務結束時沒有失敗,則 onTaskStartupexitMessage 會儲存在倉庫中。否則,如果發生失敗,則儲存 onTaskFailedexitMessage。此外,如果你使用 onTaskEnd 監聽器設定 exitMessage,則 onTaskEndexitMessage 會覆蓋 onTaskStartuponTaskFailed 的退出訊息。

限制 Spring Cloud Task 例項

Spring Cloud Task 允許你設定具有給定任務名稱的任務只能同時執行一個例項。為此,你需要確定任務名稱,併為每次任務執行設定 spring.cloud.task.single-instance-enabled=true。當第一個任務執行正在執行時,任何時候你嘗試執行具有相同任務名稱且設定了 spring.cloud.task.single-instance-enabled=true 的任務,該任務都會失敗,並顯示以下錯誤訊息:Task with name "application" is already running. spring.cloud.task.single-instance-enabled 的預設值為 false。以下示例顯示瞭如何將 spring.cloud.task.single-instance-enabled 設定為 true

spring.cloud.task.single-instance-enabled=true or false

要使用此功能,你必須嚮應用程式新增以下 Spring Integration 依賴項

<dependency>
    <groupId>org.springframework.integration</groupId>
    <artifactId>spring-integration-core</artifactId>
</dependency>
<dependency>
    <groupId>org.springframework.integration</groupId>
    <artifactId>spring-integration-jdbc</artifactId>
</dependency>
如果任務因啟用此功能且另一個具有相同任務名稱的任務正在執行而失敗,則應用程式的退出碼將為 1。

Spring AOT 和 Native Compilation 的單例項用法

在使用 Spring Cloud Task 的單例項功能建立原生編譯應用程式時,你需要在構建時啟用此功能。為此,新增 process-aot 執行,並設定 spring.cloud.task.single-step-instance-enabled=true 作為 JVM 引數,如下所示

<plugin>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-maven-plugin</artifactId>
    <executions>
        <execution>
            <id>process-aot</id>
            <goals>
                <goal>process-aot</goal>
            </goals>
            <configuration>
                <jvmArguments>
                    -Dspring.cloud.task.single-instance-enabled=true
                </jvmArguments>
            </configuration>
        </execution>
    </executions>
</plugin>

為 ApplicationRunner 和 CommandLineRunner 啟用可觀測性

要為 ApplicationRunnerCommandLineRunner 啟用任務可觀測性,請將 spring.cloud.task.observation.enabled 設定為 true。

一個啟用了可觀測性並使用 SimpleMeterRegistry 的任務應用程式示例可以在此處找到。

停用 Spring Cloud Task 自動配置

在某些實現中不應自動配置 Spring Cloud Task 的情況下,你可以停用 Task 的自動配置。這可以透過向你的 Task 應用程式新增以下註解來完成

@EnableAutoConfiguration(exclude={SimpleTaskAutoConfiguration.class})

你也可以透過將 spring.cloud.task.autoconfiguration.enabled 屬性設定為 false 來停用 Task 自動配置。

關閉上下文

如果應用程式要求在任務完成時關閉 ApplicationContext(所有 *Runner#run 方法都已呼叫且任務倉庫已更新),則將屬性 spring.cloud.task.closecontextEnabled 設定為 true

另一種需要關閉上下文的情況是,任務執行完成後,應用程式沒有終止。在這種情況下,上下文會保持開啟狀態,因為分配了執行緒(例如:如果你正在使用 TaskExecutor)。在這些情況下,在啟動任務時將 spring.cloud.task.closecontextEnabled 屬性設定為 true。這會在任務完成後關閉應用程式的上下文。從而允許應用程式終止。

啟用任務指標

Spring Cloud Task 與 Micrometer 整合,併為其執行的任務建立可觀測性資料。要啟用任務可觀測性整合,你必須向你的任務應用程式新增 spring-boot-starter-actuator、你偏好的登錄檔實現(如果你想釋出指標)以及 micrometer-tracing(如果你想釋出追蹤資料)。一個使用 Influx 啟用任務可觀測性和指標的 Maven 依賴示例如下

<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-actuator</artifactId>
</dependency>
<dependency>
    <groupId>io.micrometer</groupId>
    <artifactId>micrometer-registry-influx</artifactId>
    <scope>runtime</scope>
</dependency>

Spring Task 和 Spring Cloud Task 屬性

術語 task 在行業中是一個常用詞。例如,Spring Boot 提供了 spring.task 屬性,而 Spring Cloud Task 提供了 spring.cloud.task 屬性。過去曾因此引起一些困惑,認為這兩組屬性直接相關。然而,它們代表了 Spring 生態系統中提供的兩組不同的功能特性。

  • spring.task 指的是配置 ThreadPoolTaskScheduler 的屬性。

  • spring.cloud.task 指的是配置 Spring Cloud Task 功能特性的屬性。