功能
本节将更详细地介绍 Spring Cloud Task,包括如何使用它、如何配置它以及适当的扩展点。
Spring Cloud Task 的生命周期
在大多数情况下,现代云环境是围绕不期望结束的进程执行而设计的。如果它们确实结束了,它们通常会被重新启动。虽然大多数平台确实有某种方法可以运行一个在结束后不重新启动的进程,但该运行的结果通常不会以可消费的方式维护。Spring Cloud Task 提供了在环境中执行短生命周期进程并记录结果的能力。这样做允许围绕短生命周期进程以及通过消息集成任务来实现长时间运行服务的微服务架构。
虽然此功能在云环境中很有用,但在传统部署模型中也可能出现同样的问题。当使用 cron 等调度程序运行 Spring Boot 应用程序时,能够在应用程序完成之后监视其结果会很有用。
Spring Cloud Task 采取的方法是,Spring Boot 应用程序可以有开始和结束,并且仍然成功。批处理应用程序是预期结束(并且通常是短生命周期)的进程如何提供帮助的一个示例。
Spring Cloud Task 记录给定任务的生命周期事件。大多数长时间运行的进程(以大多数 Web 应用程序为典型)不保存其生命周期事件。Spring Cloud Task 的核心任务则会。
生命周期由单个任务执行组成。这是配置为任务(即具有 Sprint Cloud Task 依赖项)的 Spring Boot 应用程序的物理执行。
在任务开始时,在任何 CommandLineRunner 或 ApplicationRunner 实现运行之前,会在 TaskRepository 中创建一个记录开始事件的条目。此事件通过 Spring Framework 触发 SmartLifecycle#start 来触发。这表示系统所有 Bean 都已准备好使用,并且在运行 Spring Boot 提供的任何 CommandLineRunner 或 ApplicationRunner 实现之前发生。
任务的记录仅在 ApplicationContext 成功引导时发生。如果上下文完全无法引导,则不记录任务的运行。 |
在 Spring Boot 的所有 *Runner#run 调用完成后或 ApplicationContext 失败(由 ApplicationFailedEvent 指示)时,任务执行的结果会在存储库中更新。
如果应用程序要求在任务完成时(所有 *Runner#run 方法已调用且任务存储库已更新)关闭 ApplicationContext,请将属性 spring.cloud.task.closecontextEnabled 设置为 true。 |
TaskExecution
存储在 TaskRepository 中的信息在 TaskExecution 类中建模,并包含以下信息
| 字段 | 描述 |
|---|---|
|
任务运行的唯一 ID。 |
|
从 |
|
任务的名称,由配置的 |
|
任务开始时间,由 |
|
任务完成时间,由 |
|
退出时可用的任何信息。这可以通过 |
|
如果异常是任务结束的原因(由 |
|
作为命令行参数传递给可执行引导应用程序的字符串命令行参数列表。 |
映射退出代码
当任务完成时,它会尝试向操作系统返回退出代码。如果我们查看 原始示例,我们可以看到我们没有控制应用程序的这个方面。因此,如果抛出异常,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 的配置约定)。此 DataSource 将由 Spring Cloud Task 自动用于存储库。
如果您的应用程序使用多个 DataSource,则需要使用适当的 DataSource 配置任务存储库。此自定义可以通过 TaskConfigurer 的实现来完成。
表前缀
TaskRepository 的一个可修改属性是任务表的表前缀。默认情况下,它们都以 TASK_ 为前缀。TASK_EXECUTION 和 TASK_EXECUTION_PARAMS 是两个示例。但是,修改此前缀可能有一些原因。如果需要将模式名称添加到表名称前面,或者在同一模式中需要多组任务表,则必须更改表前缀。您可以通过将 spring.cloud.task.tablePrefix 设置为您需要的前缀来完成此操作,如下所示
spring.cloud.task.tablePrefix=yourPrefix
通过使用 spring.cloud.task.tablePrefix,用户承担创建符合任务表模式标准但又根据用户业务需求进行修改的任务表的责任。您可以将 Spring Cloud Task Schema DDL 作为创建您自己的任务 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 就足够了。但是,可能需要从头开始实现您自己的。
除非用户将其用于提供要作为 Spring Bean 公开的实现,否则不应直接使用 TaskConfigurer 中的 getter 方法。 |
任务执行监听器
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 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 和本机编译的单实例用法
在创建原生编译应用程序时使用 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 的自动配置。这可以通过将以下注解添加到您的任务应用程序来完成
@EnableAutoConfiguration(exclude={SimpleTaskAutoConfiguration.class})
您还可以通过将 spring.cloud.task.autoconfiguration.enabled 属性设置为 false 来禁用任务自动配置。
关闭上下文
如果应用程序要求在任务完成时(所有 *Runner#run 方法已调用且任务存储库已更新)关闭 ApplicationContext,请将属性 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>