高级元数据用法
作业注册表
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(需要一个新的 JobParameters 对象来触发新的 JobInstance)不同,如果参数与之前的任何参数集不同,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 此前从未运行过,因此可返回其初始状态。否则,将获取旧值,将其加一后返回。
-
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 最常见的用例之一是优雅地停止一个
Job:
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。