功能特性
本节将详细介绍 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 应用程序的物理执行(即,它具有 Sprint Cloud Task 依赖项)。
在任务开始时,在任何 CommandLineRunner
或 ApplicationRunner
实现运行之前,会在 TaskRepository
中创建一个记录启动事件的条目。该事件是通过 Spring 框架触发的 SmartLifecycle#start
来触发的。这向系统表明所有 bean 都已准备就绪,并且在运行 Spring Boot 提供的任何 CommandLineRunner
或 ApplicationRunner
实现之前发生。
任务的记录仅在成功引导 ApplicationContext
时发生。如果上下文完全无法引导,则不会记录任务的运行。
在完成所有来自 Spring Boot 的 *Runner#run
调用或 ApplicationContext
失败(通过 ApplicationFailedEvent
指示)后,任务执行结果将在存储库中更新。
如果应用程序需要在任务完成时关闭 ApplicationContext
(所有 *Runner#run
方法已被调用且任务仓库已更新),请将属性 spring.cloud.task.closecontextEnabled
设置为 true。
任务执行
存储在 TaskRepository
中的信息在 TaskExecution
类中进行建模,包含以下信息:
字段 | 描述 |
---|---|
executionid | 任务运行的唯一 ID。 |
exitCode | 由 ExitCodeExceptionMapper 实现生成的退出代码。如果没有生成退出代码但抛出了 ApplicationFailedEvent ,则设置为 1。否则,假定为 0。 |
taskName | 任务的名称,由配置的 TaskNameResolver 确定。 |
startTime | 任务启动的时间,由 SmartLifecycle#start 调用指示。 |
endTime | 任务完成的时间,由 ApplicationReadyEvent 指示。 |
exitMessage | 退出时可用的任何信息。可以通过 TaskExecutionListener 以编程方式设置。 |
errorMessage | 如果异常是任务结束的原因(由 ApplicationFailedEvent 指示),则该异常的堆栈跟踪存储在此处。 |
arguments | 传递给可执行引导应用程序的字符串命令行参数的 List 。 |
映射退出代码
当一个任务完成时,它会尝试向操作系统返回一个退出码。如果我们查看我们的原始示例,我们可以看到我们并没有控制我们应用程序的这方面。因此,如果抛出异常,JVM 会返回一个退出码,这个退出码在调试时可能对你有用,也可能没有用。
因此,Spring Boot 提供了一个接口 ExitCodeExceptionMapper
,允许你将未捕获的异常映射为退出码。这样做可以让你在退出码的层面上指示出了什么问题。此外,通过这种方式映射退出码,Spring Cloud Task 会记录返回的退出码。
如果任务以 SIG-INT 或 SIG-TERM 终止,退出代码为零,除非代码中另有指定。
当任务正在运行时,退出代码在存储库中存储为 null。一旦任务完成,将根据本节前面描述的指南存储适当的退出代码。
配置
Spring Cloud Task 提供了一个即用型配置,如 DefaultTaskConfigurer
和 SimpleTaskConfiguration
类中所定义。本节将介绍默认配置以及如何根据您的需求自定义 Spring Cloud Task。
数据源
Spring Cloud Task 使用数据源来存储任务执行的结果。默认情况下,我们提供了一个 H2 内存实例,以便为开发提供简单的引导方法。然而,在生产环境中,您可能需要配置自己的 DataSource
。
如果你的应用程序仅使用单个 DataSource
,并且它既作为业务模式又作为任务存储库,那么你只需要提供任何 DataSource
(最简单的方式是通过 Spring Boot 的配置约定)。Spring Cloud Task 会自动将此 DataSource
用于存储库。
如果你的应用程序使用了多个 DataSource
,你需要使用适当的 DataSource
来配置任务存储库。这种定制可以通过实现 TaskConfigurer
来完成。
表前缀
TaskRepository
的一个可修改属性是任务表的表前缀。默认情况下,它们都以 TASK_
开头。例如,TASK_EXECUTION
和 TASK_EXECUTION_PARAMS
就是两个例子。然而,有时可能需要修改这个前缀。如果需要在表名前添加模式名称,或者在同一模式中需要多组任务表,则必须更改表前缀。你可以通过设置 spring.cloud.task.tablePrefix
为你所需的前缀来实现,如下所示:
spring.cloud.task.tablePrefix=yourPrefix
通过使用 spring.cloud.task.tablePrefix
,用户承担了创建任务表的责任,这些表需要满足任务表模式的标准,同时还需要根据用户的业务需求进行修改。在创建自己的任务 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
。
可以通过在实现 TaskRepository
的实例上调用 createTaskExecution
方法来创建一个 TaskExecution
,该 TaskRepository
引用了存储 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
。
用户不应直接使用 TaskConfigurer
的 getter 方法,除非他们使用它来提供实现并将其暴露为 Spring Bean。
任务执行监听器
TaskExecutionListener
允许你为任务生命周期中发生的特定事件注册监听器。为此,创建一个实现 TaskExecutionListener
接口的类。实现 TaskExecutionListener
接口的类会收到以下事件的通知:
-
onTaskStartup
: 在将TaskExecution
存储到TaskRepository
之前。 -
onTaskEnd
: 在更新TaskRepository
中的TaskExecution
条目并标记任务的最终状态之前。 -
onTaskFailed
: 在任务抛出未处理的异常时,调用onTaskEnd
方法之前。
Spring Cloud Task 还允许你通过以下方法注解将 TaskExecution
监听器添加到 bean 中的方法上:
-
@BeforeTask
: 在将TaskExecution
存储到TaskRepository
之前执行。 -
@AfterTask
: 在更新TaskRepository
中的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
方法。然而,TaskExecutionListeners
的其他事件处理程序(onTaskEnd
和 onTaskFailed
)仍会被调用。
当 TaskExecutionListener
事件处理程序抛出异常时返回的退出码是由 ExitCodeEvent 报告的退出码。如果没有发出 ExitCodeEvent
,则会评估抛出的异常,以查看它是否属于 ExitCodeGenerator 类型。如果是,则返回 ExitCodeGenerator
中的退出码。否则,返回 1
。
如果在 onTaskStartup
方法中抛出异常,应用程序的退出代码将为 1
。如果在 onTaskEnd
或 onTaskFailed
方法中抛出异常,应用程序的退出代码将根据上述规则确定。
如果在 onTaskStartup
、onTaskEnd
或 onTaskFailed
中抛出异常,你不能使用 ExitCodeExceptionMapper
来覆盖应用程序的退出代码。
退出消息
你可以通过使用 TaskExecutionListener
以编程方式设置任务的退出消息。这是通过设置 TaskExecution
的 exitMessage
来实现的,然后该消息会传递给 TaskExecutionListener
。以下示例展示了一个使用 @AfterTask
ExecutionListener
注解的方法:
@AfterTask
public void afterTask(TaskExecution taskExecution) {
taskExecution.setExitMessage("Task completed successfully");
}
@AfterTask
public void afterMe(TaskExecution taskExecution) {
taskExecution.setExitMessage("AFTER EXIT MESSAGE");
}
可以在任何监听器事件(onTaskStartup
、onTaskFailed
和 onTaskEnd
)中设置 ExitMessage
。这三个监听器的优先级顺序如下:
-
onTaskEnd
-
onTaskFailed
-
onTaskStartup
这些是任务生命周期中的事件回调函数,分别表示任务结束、任务失败和任务启动时的处理逻辑。
例如,如果你为 onTaskStartup
和 onTaskFailed
监听器设置了 exitMessage
,并且任务在没有失败的情况下结束,那么 onTaskStartup
的 exitMessage
将被存储在仓库中。否则,如果发生失败,onTaskFailed
的 exitMessage
将被存储。此外,如果你使用 onTaskEnd
监听器设置了 exitMessage
,那么 onTaskEnd
的 exitMessage
将取代 onTaskStartup
和 onTaskFailed
的退出消息。
限制 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 或 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 和原生编译的单例用法
要在创建原生编译应用时使用 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 启用观察
要为 ApplicationRunner
或 CommandLineRunner
启用任务观察,请将 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 功能的属性。