Features

本节将更详细地介绍 Spring Cloud Task,包括如何使用、如何配置它以及合适的扩展点。

The lifecycle of a 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 应用程序的物理执行。

在任务开始时,在运行任何 CommandLineRunnerApplicationRunner 实现之前,将创建 TaskRepository 中记录开始事件的条目。此事件通过 Spring Framework 触发 SmartLifecycle#start 触发。这向系统指示所有 bean 均已准备就绪,并且在运行 Spring Boot 提供的任何 CommandLineRunnerApplicationRunner 实现之前。

任务的记录只在 ApplicationContext 的成功引导时发生。如果上下文的引导完全失败,则任务的运行不会被记录。

完成 Spring Boot 或 ApplicationContext 的所有 *Runner#run 调用(由 ApplicationFailedEvent 指示)后,任务执行将在存储库中更新结果。

如果应用程序要求任务完成后关闭 ApplicationContext(所有 *Runner#run 方法都已调用,并且已更新任务存储库),请将属性 spring.cloud.task.closecontextEnabled 设置为 true。

The TaskExecution

存储在 TaskRepository 中的信息在 TaskExecution 类中建模,并包含以下信息:

Field Description

executionid

该任务运行的唯一 ID。

exitCode

ExitCodeExceptionMapper 实现中生成的退出代码。如果没有生成退出代码但抛出 ApplicationFailedEvent,则设置为 1。否则,假设为 0。

taskName

任务的名称,由配置的 TaskNameResolver 确定。

startTime

任务的开始时间,由 SmartLifecycle#start 调用指示。

endTime

任务完成的时间,由 ApplicationReadyEvent 指示。

exitMessage

退出时可用的任何信息。这可以通过 TaskExecutionListener 以编程方式设置。

errorMessage

如果异常是任务结束的原因(如 ApplicationFailedEvent 所示),则此处的异常的堆栈跟踪将存储在这里。

arguments

字符串命令行参数的 List,因为它们被传递到可执行引导应用程序中。

Mapping Exit Codes

任务完成后,它会尝试向操作系统返回退出代码。如果我们看一下我们的 original example,我们可以看到我们无法控制我们应用程序的该方面。因此,如果抛出异常,JVM 会返回一个代码,但该代码对于你的调试来说可能有用或无用。

因此,Spring Boot 提供了一个界面 ExitCodeExceptionMapper,它允许你将未捕获的异常映射到退出代码。这样做让你能够以退出代码的级别指示出现什么问题。此外,通过这种方式映射退出代码,Spring Cloud Task 记录返回的退出代码。

如果任务以 SIG-INT 或 SIG-TERM 终止,则退出代码为零,除非代码中另有指定。

在任务运行期间,退出代码作为 null 存储在存储库中。任务完成后,将根据本节前面描述的准则存储适当的退出代码。

Configuration

Spring Cloud Task 提供了开箱即用的配置,如 DefaultTaskConfigurerSimpleTaskConfiguration 类中定义的配置。本节将介绍默认值以及如何根据需要自定义 Spring Cloud Task。

DataSource

Spring Cloud Task 使用数据源存储任务执行的结果。默认情况下,我们提供 H2 的内存中实例,以提供一种简单的引导开发方法。但是,在生产环境中,你可能希望配置自己的 DataSource

如果你的应用只使用一个 DataSource,既可以用作业务模式,又可以用作任务存储库,那么你只需提供一个 DataSource(最简单的方式是通过 Spring Boot 的配置约定)。这个 DataSource 会被 Spring Cloud Task 自动用作存储库。

如果你的应用使用多个 DataSource,则需要使用合适的 DataSource 来配置任务存储库。这种自定义可以通过 TaskConfigurer 的实现来完成。

Table Prefix

TaskRepository 可修改的属性之一是任务表的表前缀。默认情况下,所有前缀都是 TASK_TASK_EXECUTIONTASK_EXECUTION_PARAMS 就是两个例子。不过,可能出于多种原因需要修改此前缀。如果需要将模式名称添加到表名之前,或者在同一模式中需要多组任务表,你必须更改表前缀。你可以通过将 spring.cloud.task.tablePrefix 设置为所需的表前缀来完成此操作,如下所示:

spring.cloud.task.tablePrefix=yourPrefix

通过使用 spring.cloud.task.tablePrefix,用户承担责任来创建符合任务表架构标准的任务表,但会做出用户业务需要的一些修改。在创建自己的任务 DDL 时,您可以将 Spring Cloud 任务架构 DDL 用作指南,如 here 所示。

Enable/Disable table initialization

如果你要创建任务表,但不希望 Spring Cloud Task 在任务启动时创建这些表,请将 spring.cloud.task.initialize-enabled 属性设置为 false,如下所示:

spring.cloud.task.initialize-enabled=false

默认值为 true

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

Externally Generated Task ID

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

可以通过调用引用存储 TaskExecution 对象的数据存储的 TaskRepository 实现中的 createTaskExecution 方法来创建一个 TaskExecution

为了配置你的任务以使用生成的 TaskExecutionId,添加如下属性:

spring.cloud.task.executionid=yourtaskId

External Task Id

Spring Cloud Task 让你可以为每个 TaskExecution 存储一个外部任务 ID。为了配置你的任务以使用生成的 TaskExecutionId,添加如下属性:

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

Parent Task 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 让你可以配置三个主要组件:

Component Description Default (provided by DefaultTaskConfigurer)

TaskRepository

要使用的 TaskRepository 的实现。

SimpleTaskRepository

TaskExplorer

用于实现 TaskExplorer (仅用于读取 taskrepository 的组件)。

SimpleTaskExplorer

PlatformTransactionManager

运行任务更新时要使用的交易管理器。

如果使用 DataSource,则使用 JdbcTransactionManager;如果未使用,则不使用。

你可以通过创建 TaskConfigurer 接口的自定义实现来自定义前表中描述的任何组件。通常,扩展 DefaultTaskConfigurer(在找不到 TaskConfigurer 时提供)并重写所需的 getter 就可以了。不过,这可能还需要从头开始实现你自己的任务。

除非用户正在使用它来提供要作为 Spring Bean 公开的实现,否则不应直接使用 TaskConfigurer 的 getter 方法。

Task Execution Listener

TaskExecutionListener 让你可以为任务生命周期中发生的特定事件注册侦听器。要进行此操作,请创建一个实现 TaskExecutionListener 接口的类。实现 TaskExecutionListener 接口的类会收到以下事件的通知:

  • onTaskStartup:在 TaskRepository 中存储 TaskExecution 之前。

  • onTaskEnd:在 TaskRepository 中更新 TaskExecution 条目之前,并标记任务的最终状态。

  • onTaskFailed:在任务抛出未处理异常时调用 onTaskEnd 方法之前。

Spring Cloud Task 也允许您使用以下方法注释将 TaskExecution 事件侦听器添加到 bean 中的方法:

  • @BeforeTask:在 TaskRepository 中存储 TaskExecution 之前。

  • @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 可能会导致意外的影响。

Exceptions Thrown by Task Execution Listener

如果 TaskExecutionListener 事件处理程序引发异常,则该事件处理程序的所有侦听器处理将停止。例如,如果三个 onTaskStartup 侦听器已经启动,并且第一个 onTaskStartup 事件处理程序引发异常,则不会调用其他两个 onTaskStartup 方法。但是,TaskExecutionListeners 的其他事件处理程序(onTaskEndonTaskFailed)将被调用。

TaskExecutionListener`事件处理程序抛出异常时返回的退出代码是 ExitCodeEvent 报告的退出代码。如果没有发出 `ExitCodeEvent,则会评估抛出的异常以查看它是否为 ExitCodeGenerator 类型。如果是,则它返回来自 ExitCodeGenerator 的退出代码。否则,返回 1

如果在 onTaskStartup 方法中引发异常,则该应用程序的退出代码将为 1。如果在 onTaskEndonTaskFailed 方法中引发异常,则该应用程序的退出代码将是使用上述规则确立的代码。

如果在 onTaskStartuponTaskEndonTaskFailed 中抛出异常,则无法使用 ExitCodeExceptionMapper 覆盖应用程序的退出代码。

Exit Messages

您可以使用 TaskExecutionListener 以编程方式设置任务的退出消息。这是通过设置 TaskExecution’s exitMessage 完成的,然后将其传递给 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 的退出消息。

Restricting Spring Cloud Task Instances

Spring Cloud Task 允许您确定一次只能运行一个具有给定任务名称的任务。为此,您需要为每个任务执行建立 features-task-name 并在 cloud-task 中设置 spring.cloud.task.single-instance-enabled=true。虽然第一个任务执行正在运行,但您尝试重新运行具有相同 features-task-namespring.cloud.task.single-instance-enabled=true 的任务的任何其他时间,则该任务将失败,并出现以下错误消息:Task with name "application" is alreadyrunning. 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。

Single Instance Usage for Spring AOT And 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>

Enabling Observations for ApplicationRunner and CommandLineRunner

要为 ApplicationRunnerCommandLineRunner 启用任务观察,请将 spring.cloud.task.observation.enabled 设置为 true。

带观测功能的示例任务应用程序允许使用 SimpleMeterRegistry 可在 here 中找到。

Disabling Spring Cloud Task Auto Configuration

如果 Spring Cloud Task 不应针对某个实现进行自动配置,则可以禁用任务的自动配置。这可以通过将以下注释添加到您的任务应用程序来实现:

@EnableAutoConfiguration(exclude={SimpleTaskAutoConfiguration.class})

您还可以通过将 spring.cloud.task.autoconfiguration.enabled 属性设置为 false 来禁用 Task 自动配置。

Closing the Context

如果应用程序需要在任务完成后关闭 ApplicationContext(所有 *Runner#run 方法都已调用并且任务存储库已更新),请将属性 spring.cloud.task.closecontextEnabled 设置为 true

关闭上下文的另一个情况是当任务执行完成但应用程序未终止时。在这些情况下,上下会保持开启状态,因为已分配了一个线程(例如:如果您正在使用 TaskExecutor)。在这些情况下,请在启动任务时将 spring.cloud.task.closecontextEnabled 属性设置为 true。这将在任务完成后关闭应用程序的上下文。从而允许应用程序终止。

Enable Task Metrics

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 and Spring Cloud Task Properties

该行业中经常使用术语“task”。在一个这样的示例中,Spring Boot 提供 spring.task,而 Spring Cloud Task 提供 spring.cloud.task 属性。过去,这引起了某种困惑,认为这两组属性直接相关。然而,它们代表了 Spring 生态系统中提供的两组不同的功能。

  • spring.task 指代配置 ThreadPoolTaskScheduler 的属性。

  • spring.cloud.task 指代配置 Spring Cloud Task 功能的属性。