功能

本节将更详细地介绍 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 依赖项)。

在任务开始时,在任何 CommandLineRunnerApplicationRunner 实现运行之前,会创建 TaskRepository 中的条目以记录启动事件。此事件是通过 Spring 框架触发的 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

传递到可执行引导应用程序的字符串命令行参数的 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 并且该数据源同时作为您的业务架构和任务存储库,则您需要做的就是提供任何 DataSource(最简单的方法是通过 Spring Boot 的配置约定)。此 DataSource 会自动由 Spring Cloud Task 用于存储库。

如果您的应用程序使用多个 DataSource,则需要使用相应的 DataSource 配置任务存储库。可以通过 TaskConfigurer 的实现来完成此自定义。

表前缀

TaskRepository 的一个可修改属性是任务表的表前缀。默认情况下,它们都以 TASK_ 为前缀。TASK_EXECUTIONTASK_EXECUTION_PARAMS 是两个示例。但是,可能有修改此前缀的潜在原因。如果架构名称需要附加到表名,或者如果在同一架构中需要多组任务表,则必须更改表前缀。您可以通过将 spring.cloud.task.tablePrefix 设置为您需要的 前缀来实现,如下所示

spring.cloud.task.tablePrefix=yourPrefix

通过使用 spring.cloud.task.tablePrefix,用户承担了创建满足任务表架构标准但同时满足用户业务需求的修改的任务表的责任。在创建自己的任务 DDL 时,您可以使用 Spring Cloud Task 架构 DDL 作为指南,如 此处 所示。

启用/禁用表初始化

如果您正在创建任务表并且不希望 Spring Cloud Task 在任务启动时创建它们,请将spring.cloud.task.initialize-enabled 属性设置为false,如下所示

spring.cloud.task.initialize-enabled=false

其默认值为true

属性spring.cloud.task.initialize.enable 已弃用。

外部生成的 Task ID

在某些情况下,您可能希望允许任务被请求与基础设施实际启动它之间的时间差。Spring Cloud Task 允许您在请求任务时创建TaskExecution。然后将生成的TaskExecution 的执行 ID 传递给任务,以便它可以通过任务的生命周期更新TaskExecution

可以通过在TaskRepository 的实现上调用createTaskExecution 方法来创建TaskExecution,该实现引用保存TaskExecution 对象的数据存储。

为了配置您的 Task 以使用生成的TaskExecutionId,请添加以下属性

spring.cloud.task.executionid=yourtaskId

外部 Task ID

Spring Cloud Task 允许您为每个TaskExecution 存储一个外部任务 ID。为了配置您的 Task 以使用生成的TaskExecutionId,请添加以下属性

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

父任务 ID

Spring Cloud Task 允许您为每个TaskExecution 存储一个父任务 ID。例如,一个任务执行另一个任务或多个任务,并且您希望记录哪个任务启动了每个子任务。为了配置您的 Task 以设置父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 公开的实现。

Task Execution Listener

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 可能导致意外效果。

Task Execution Listener 抛出的异常

如果TaskExecutionListener 事件处理程序抛出异常,则该事件处理程序的所有监听器处理都会停止。例如,如果三个onTaskStartup 监听器已启动并且第一个onTaskStartup 事件处理程序抛出异常,则不会调用其他两个onTaskStartup 方法。但是,会调用TaskExecutionListeners 的其他事件处理程序(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 或 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 启用观测

要为ApplicationRunnerCommandLineRunner 启用 Task 观测,请将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 功能的属性。