功能特性
本节更详细地介绍了 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 依赖项)。
在任务开始时,在任何 CommandLineRunner 或 ApplicationRunner 实现运行之前,会在 TaskRepository 中创建一个记录开始事件的条目。此事件通过 Spring Framework 触发 SmartLifecycle#start 来触发。这向系统表明所有 bean 都已准备就绪,并且在此之前会运行 Spring Boot 提供的任何 CommandLineRunner 或 ApplicationRunner 实现。
任务的记录仅在 ApplicationContext 成功引导时发生。如果上下文完全无法引导,则不记录该任务的运行。 |
在 Spring Boot 的所有 *Runner#run 调用完成后,或者 ApplicationContext 失败(由 ApplicationFailedEvent 指示)时,任务执行的结果会在仓库中更新。
如果应用程序要求在任务完成时关闭 ApplicationContext(所有 *Runner#run 方法都已调用且任务仓库已更新),则将属性 spring.cloud.task.closecontextEnabled 设置为 true。 |
TaskExecution
存储在 TaskRepository 中的信息在 TaskExecution 类中建模,包含以下信息
| 字段 | 描述 |
|---|---|
|
任务运行的唯一 ID。 |
|
由 |
|
任务的名称,由配置的 |
|
任务启动的时间,由 |
|
任务完成的时间,由 |
|
退出时可用的任何信息。这可以通过 |
|
如果异常是任务结束的原因(由 |
|
传递给可执行 boot 应用程序的字符串命令行参数的 |
映射退出码
当任务完成时,它会尝试向操作系统返回一个退出码。如果我们看看我们的原始示例,可以看到我们没有控制应用程序的这一方面。因此,如果抛出异常,JVM 返回的代码可能对你的调试有用,也可能没有用。
因此,Spring Boot 提供了一个接口 ExitCodeExceptionMapper,允许你将未捕获的异常映射到退出码。这样做可以让你在退出码级别指示出了什么问题。此外,通过以这种方式映射退出码,Spring Cloud Task 会记录返回的退出码。
如果任务因 SIG-INT 或 SIG-TERM 而终止,退出码为零,除非代码中另有指定。
| 任务运行时,退出码在仓库中存储为 null。任务完成后,根据本节前面描述的指南存储相应的退出码。 |
配置
Spring Cloud Task 提供了开箱即用的配置,如 DefaultTaskConfigurer 和 SimpleTaskConfiguration 类中定义的那样。本节将介绍默认配置以及如何根据需要自定义 Spring Cloud Task。
DataSource
Spring Cloud Task 使用数据源来存储任务执行结果。默认情况下,我们提供了一个内存中的 H2 实例,以提供简单的开发引导方法。然而,在生产环境中,你可能希望配置自己的 DataSource。
如果你的应用程序只使用一个 DataSource,并且它既作为你的业务 schema 又作为任务仓库,你只需提供任何 DataSource(最简单的方法是通过 Spring Boot 的配置约定)。Spring Cloud Task 会自动将此 DataSource 用于仓库。
如果你的应用程序使用多个 DataSource,你需要使用适当的 DataSource 配置任务仓库。此自定义可以通过 TaskConfigurer 的实现来完成。
表前缀
TaskRepository 的一个可修改属性是任务表的表前缀。默认情况下,它们都以 TASK_ 开头。TASK_EXECUTION 和 TASK_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 提供) |
|---|---|---|
|
要使用的 |
|
|
要使用的 |
|
|
执行任务更新时使用的事务管理器。 |
如果使用 |
你可以通过创建 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 的其他事件处理程序(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 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 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 启用可观测性
要为 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>