高级元数据使用
JobRegistry
JobRegistry 用于跟踪上下文中哪些作业可用,并可由 JobOperator 操作。它也适用于在应用程序上下文中集中收集作业,即使它们是在其他地方(例如,在子上下文)创建的。您还可以使用自定义 JobRegistry 实现来操作已注册作业的名称和其他属性。框架只提供了一个实现,该实现基于从作业名称到作业实例的简单映射,即 MapJobRegistry。
-
Java
-
XML
使用 @EnableBatchProcessing 时,会为您提供一个 MapJobRegistry。以下示例展示了如何配置您自己的 JobRegistry
...
@Bean
public JobRegistry jobRegistry() throws Exception {
return new MyCustomJobRegistry();
}
...
以下示例展示了如何为 XML 中定义的作业包含 JobRegistry
<bean id="jobRegistry" class="org.springframework.batch.core.configuration.support.MapJobRegistry" />
Spring Batch 提供的 MapJobRegistry 足够智能,可以自动填充应用程序上下文中所有作业。但是,如果您使用 JobRegistry 的自定义实现,则需要手动填充要通过作业操作器操作的作业。
JobParametersIncrementer
JobOperator 中的大多数方法都一目了然,您可以在接口的 Javadoc 中找到更详细的解释。但是,startNextInstance 方法值得注意。此方法始终启动 Job 的新实例。如果 JobExecution 中存在严重问题并且需要从头开始重新启动 Job,这会非常有用。与 JobLauncher(它需要一个触发新 JobInstance 的新 JobParameters 对象)不同,如果参数与任何以前的参数集不同,startNextInstance 方法使用绑定到 Job 的 JobParametersIncrementer 来强制 Job 到一个新的实例
public interface JobParametersIncrementer {
JobParameters getNext(JobParameters parameters);
}
JobParametersIncrementer 的契约是,给定一个 JobParameters 对象,它通过递增其中可能包含的任何必要值来返回“下一个”JobParameters 对象。此策略很有用,因为框架无法知道对 JobParameters 的哪些更改使其成为“下一个”实例。例如,如果 JobParameters 中唯一的值是日期,并且应该创建下一个实例,那么该值应该增加一天还是一周(例如,如果作业是每周运行的)?同样,对于任何有助于识别 Job 的数值也可以这样说,如下例所示
public class SampleIncrementer implements JobParametersIncrementer {
public JobParameters getNext(JobParameters parameters) {
if (parameters==null || parameters.isEmpty()) {
return new JobParametersBuilder().addLong("run.id", 1L).toJobParameters();
}
long id = parameters.getLong("run.id",1L) + 1;
return new JobParametersBuilder().addLong("run.id", id).toJobParameters();
}
}
在此示例中,键为 run.id 的值用于区分 JobInstances。如果传入的 JobParameters 为 null,则可以假定该 Job 从未运行过,因此可以返回其初始状态。但是,如果不是,则获取旧值,将其递增 1,然后返回。
-
Java
-
XML
对于用 Java 定义的作业,您可以通过构建器中提供的 incrementer 方法将增量器与 Job 相关联,如下所示
@Bean
public Job footballJob(JobRepository jobRepository) {
return new JobBuilder("footballJob", jobRepository)
.incrementer(sampleIncrementer())
...
.build();
}
对于在 XML 中定义的作业,您可以通过命名空间中的 incrementer 属性将增量器与 Job 相关联,如下所示
<job id="footballJob" incrementer="sampleIncrementer">
...
</job>
停止作业
JobOperator 最常见的用例之一是优雅地停止作业
Set<Long> executions = jobOperator.getRunningExecutions("sampleJob");
jobOperator.stop(executions.iterator().next());
关闭不是立即的,因为无法强制立即关闭,特别是如果执行当前在开发人员代码中,而框架无法控制这些代码,例如业务服务。但是,一旦控制权返回给框架,它会将当前 StepExecution 的状态设置为 BatchStatus.STOPPED,保存它,并在完成之前对 JobExecution 执行相同的操作。
处理外部中断信号
从 v6.0+ 开始,Spring Batch 提供了一个 JobExecutionShutdownHook,您可以将其附加到 JVM 运行时,以拦截外部中断信号并优雅地停止作业执行
Thread springBatchHook = new JobExecutionShutdownHook(jobExecution, jobOperator);
Runtime.getRuntime().addShutdownHook(springBatchHook);
JobExecutionShutdownHook 需要跟踪作业执行,以及对将用于停止执行的作业操作器的引用。
恢复作业
如果未正确执行优雅关闭(即 JVM 突然关闭),Spring Batch 将无法正确更新执行状态以重新启动失败的作业执行。在这种情况下,作业执行将保持在 STARTED 状态,该状态不可重新启动。在这种情况下,可以使用 JobOperator API 恢复此类作业执行
JobExecution jobExecution = ...; // get the job execution to recover
jobOperator.recover(jobExecution);
jobOperator.restart(jobExecution);
中止作业
状态为 FAILED 的作业执行可以重新启动(如果 Job 可重新启动)。状态为 ABANDONED 的作业执行不能由框架重新启动。ABANDONED 状态也用于步骤执行中,以将其标记为在重新启动的作业执行中可跳过。如果作业正在运行并遇到在之前失败的作业执行中标记为 ABANDONED 的步骤,它将继续执行下一个步骤(由作业流定义和步骤执行退出状态确定)。
如果进程死亡(kill -9 或服务器故障),作业当然没有运行,但 JobRepository 无法知道,因为在进程死亡之前没有人告诉它。您必须手动告知它,您知道执行要么失败,要么应该被视为中止(将其状态更改为 FAILED 或 ABANDONED)。这是一个业务决策,无法自动化。仅当可重新启动且您知道重新启动数据有效时,才将状态更改为 FAILED。