4. 配置和运行 Job

domain 部分中,讨论了整个 architecture 设计,使用下图作为指南:

虽然Job object 看起来像是一个简单的步骤容器,但是开发人员必须注意许多 configuration 选项。此外,关于将如何运行以及如何在运行期间存储 meta-data 有很多考虑因素。本章将解释Job的各种 configuration 选项和运行时关注点。

4.1 配置 Job

工作接口有多个 implementation,但是,命名空间抽象出 configuration 中的差异。它只有三个必需的依赖项:name,JobRepositoryStep s 列表。

<job id="footballJob">
    <step id="playerload"          parent="s1" next="gameLoad"/>
    <step id="gameLoad"            parent="s2" next="playerSummarization"/>
    <step id="playerSummarization" parent="s3"/>
</job>

这里的示例使用 parent bean 定义来创建步骤;请参阅step configuration部分以获取更多选项,以内联方式声明特定的 step 详细信息。 XML 命名空间默认引用 id 为'jobRepository'的 repository,这是一个合理的默认值。但是,这可以明确地重写:

<job id="footballJob" job-repository="specialRepository">
    <step id="playerload"          parent="s1" next="gameLoad"/>
    <step id="gameLoad"            parent="s3" next="playerSummarization"/>
    <step id="playerSummarization" parent="s3"/>
</job>

除了步骤之外,job configuration 还可以包含其他有助于并行化(<split/>),声明性流控制(<decision/>)和流定义外部化(<flow/>)的元素。

4.1.1 可重启性

执行批处理 job 时的一个 key 问题涉及重启时Job的行为。如果特定JobInstance已经存在JobExecution,则Job的启动被认为是'重启'。理想情况下,所有工作都应该能够在他们中断的地方启动,但有些情况下这是不可能的。 完全取决于开发人员确保在此方案中创建新的 JobInstance。但是,Spring Batch 确实提供了一些帮助。如果永远不应该重新启动Job,但是应该始终将 run 作为新JobInstance的一部分,则可重新启动的 property 可以设置为'false':

<job id="footballJob" restartable="false">
    ...
</job>

换句话说,将 restartable 设置为 false 意味着“此 Job 不支持再次启动”。重新启动不可重新启动的 Job 将导致抛出JobRestartException

Job job = new SimpleJob();
job.setRestartable(false);

JobParameters jobParameters = new JobParameters();

JobExecution firstExecution = jobRepository.createJobExecution(job, jobParameters);
jobRepository.saveOrUpdate(firstExecution);

try {
    jobRepository.createJobExecution(job, jobParameters);
    fail();
}
catch (JobRestartException e) {
    // expected
}

这段 JUnit code 显示了如何尝试为不可重启的job创建JobExecution第一个 time 将不会导致任何问题。但是,第二次尝试将抛出JobRestartException

4.1.2 拦截 Job 执行

在执行Job的过程中,在其生命周期中通知各种 events 以便可以执行 custom code 可能是有用的。 SimpleJob允许通过在适当的 time 调用JobListener来实现:

public interface JobExecutionListener {

    void beforeJob(JobExecution jobExecution);

    void afterJob(JobExecution jobExecution);

}

可以通过 job 上的 listeners 元素将JobListener添加到SimpleJob

<job id="footballJob">
    <step id="playerload"          parent="s1" next="gameLoad"/>
    <step id="gameLoad"            parent="s2" next="playerSummarization"/>
    <step id="playerSummarization" parent="s3"/>
    <listeners>
        <listener ref="sampleListener"/>
    </listeners>
</job>

应该注意的是,无论Job的成功与否,都将调用afterJob。如果需要确定成功或失败,可以从JobExecution获得:

public void afterJob(JobExecution jobExecution){
    if( jobExecution.getStatus() == BatchStatus.COMPLETED ){
        //job success
    }
    else if(jobExecution.getStatus() == BatchStatus.FAILED){
        //job failure
    }
}

与此接口对应的注释是:

  • @BeforeJob

  • @AfterJob

4.1.3 从 Parent Job 继承

如果Job的 group 共享相似但不相同的配置,那么定义具体Job可以继承 properties 的“parent”Job可能会有所帮助。与 Java 中的 class 继承类似,“ child”Job将其元素和属性与 parent 组合在一起。

在下面的示例中,“baseJob”是一个抽象的Job定义,它只定义 listeners 列表。 Job“job1”是一个具体的定义,它继承了“baseJob”中的 listeners 列表,并将其与自己的 listeners 列表合并,生成一个Job,其中包含两个 listeners 和一个Step,“step1”。

<job id="baseJob" abstract="true">
    <listeners>
        <listener ref="listenerOne"/>
    <listeners>
</job>

<job id="job1" parent="baseJob">
    <step id="step1" parent="standaloneStep"/>

    <listeners merge="true">
        <listener ref="listenerTwo"/>
    <listeners>
</job>

有关更多详细信息,请参阅继承 Parent Step部分。

4.1.4 JobParametersValidator

在 XML 命名空间中声明的 job 或使用 AbstractJob 的任何子类可以选择在运行时声明 job 参数的验证器。例如,当您需要断言 job 以其所有必需参数启动时,这非常有用。 DefaultJobParametersValidator 可用于约束简单强制参数和可选参数的组合,对于更复杂的约束,您可以自己实现接口。通过 job,e.g 的 child 元素通过 XML 命名空间支持验证器的 configuration:

<job id="job1" parent="baseJob3">
    <step id="step1" parent="standaloneStep"/>
    <validator ref="paremetersValidator"/>
</job>

验证器可以指定为 reference(如上所述)或 beans 命名空间中的嵌套 bean 定义。

4.2 Java 配置

Spring 3 带来了通过 java 而不是 XML 配置 applications 的能力。从 Spring Batch 2.2.0 开始,可以使用相同的 java 配置配置批处理作业。基于 java 的 configuration 有两个组件:@EnableBatchConfiguration annotation 和两个构建器。

@EnableBatchProcessing的工作方式与 Spring 系列中的其他@Enable* 注解类似。在这种情况下,@EnableBatchProcessing为 building 批处理作业提供了基本配置。在此基本 configuration 中,除了许多可用于自动装配的 beans 之外,还创建了StepScope的实例:

  • JobRepository - bean name“jobRepository”

  • JobLauncher - bean name“jobLauncher”

  • JobRegistry - bean name“jobRegistry”

  • PlatformTransactionManager - bean name“transactionManager”

  • JobBuilderFactory - bean name“jobBuilders”

  • StepBuilderFactory - bean name“stepBuilders”

此 configuration 的核心接口是BatchConfigurer。默认的 implementation 提供了上面提到的 beans,并且要在 context 中提供DataSource作为 bean。该数据源将由JobRepository使用。

只有一个 configuration class 需要@EnableBatchProcessing annotation。一旦你用 class 注释它,你就可以获得以上所有内容。

通过 base configuration,用户可以使用提供的构建器工厂来配置 job。下面是通过JobBuilderFactoryStepBuilderFactory配置的两个 step job 的示例。

@Configuration
@EnableBatchProcessing
@Import(DataSourceConfiguration.class)
public class AppConfig {

    @Autowired
    private JobBuilderFactory jobs;

    @Autowired
    private StepBuilderFactory steps;

    @Bean
    public Job job(@Qualifier("step1") Step step1, @Qualifier("step2") Step step2) {
        return jobs.get("myJob").start(step1).next(step2).build();
    }

    @Bean
    protected Step step1(ItemReader<Person> reader, ItemProcessor<Person, Person> processor, ItemWriter<Person> writer) {
        return steps.get("step1")
            .<Person, Person> chunk(10)
            .reader(reader)
            .processor(processor)
            .writer(writer)
            .build();
    }

    @Bean
    protected Step step2(Tasklet tasklet) {
        return steps.get("step2")
            .tasklet(tasklet)
            .build();
    }
}

4.3 配置 JobRepository

如前所述,JobRepository用于 Spring Batch 中各种持久 domain objects 的基本 CRUD 操作,例如JobExecutionStepExecution。许多主要的 framework features 都需要它,例如JobLauncherJobStep。批处理命名空间抽象出JobRepository __mplementations 及其协作者的许多 implementation 细节。但是,仍然有一些 configuration 选项可用:

<job-repository id="jobRepository"
    data-source="dataSource"
    transaction-manager="transactionManager"
    isolation-level-for-create="SERIALIZABLE"
    table-prefix="BATCH_"
	max-varchar-length="1000"/>

除了 id 之外,上面列出的任何 configuration 选项都是必需的。如果未设置,将使用上面显示的默认值。出于意识目的,它们在上面显示。 max-varchar-length默认为 2500,这是sample schema 脚本中 long VARCHAR列的长度
用于存储退出 code 描述之类的东西。如果您不修改 schema 并且不使用 multi-byte 字符,则不需要更改它。

4.3.1 Transaction Configuration for JobRepository

如果使用命名空间,将在 repository 周围自动创建 transactional 建议。这是为了确保批处理元数据(包括失败后重新启动所需的 state)正确保留。如果 repository 方法不是 transactional,则 framework 的行为没有很好地定义。 create*方法属性中的隔离 level 是单独指定的,以确保在启动作业时,如果两个进程尝试在同一 time 启动相同的 job,则只有一个成功。该方法的默认隔离 level 是 SERIALIZABLE,这是非常激进的:READ_COMMITTED 也可以正常工作;如果两个进程不可能以这种方式发生碰撞,那么 READ_UNCOMMITTED 就没问题了。但是,由于对create*方法的调用非常短,因此 SERIALIZED 不太可能导致问题,因为数据库平台支持它。但是,这可以被覆盖:

<job-repository id="jobRepository"
                isolation-level-for-create="REPEATABLE_READ" />

如果未使用名称空间或工厂 beans,那么使用 AOP 配置 repository 的 transactional 行为也很重要:

<aop:config>
    <aop:advisor
           pointcut="execution(* org.springframework.batch.core..*Repository+.*(..))"/>
    <advice-ref="txAdvice" />
</aop:config>

<tx:advice id="txAdvice" transaction-manager="transactionManager">
    <tx:attributes>
        <tx:method name="*" />
    </tx:attributes>
</tx:advice>

这个片段可以按原样使用,几乎没有变化。还要记住包含适当的命名空间声明,并确保 spring-tx 和 spring-aop(或整个 spring)在 classpath 上。

4.3.2 更改 Table 前缀

JobRepository的另一个可修改的 property 是 meta-data 表的 table 前缀。默认情况下,它们都以 BATCH开头。 BATCHJOB_EXECUTION 和 BATCHSTEP_EXECUTION 是两个例子。但是,有可能会修改此前缀。如果需要将 schema 名称添加到 table 名称之前,或者在同一 schema 中需要多组元数据表,则需要更改 table 前缀:

<job-repository id="jobRepository"
                table-prefix="SYSTEM.TEST_" />

鉴于上述更改,对元数据表的每个查询都将以“SYSTEM. TEST_”作为前缀。 BATCHJOB_EXECUTION 将被称为 SYSTEM.TESTJOB_EXECUTION。

只有 table 前缀是可配置的。 table 和列名称不是。

4.3.3 In-Memory Repository

在某些情况下,您可能不希望将 domain objects 持久保存到数据库中。一个原因可能是速度;在每个提交点存储 domain objects 需要额外的 time。另一个原因可能是您不需要为特定的 job 保留状态。因此,Spring batch 提供 job repository 的 in-memory Map version:

<bean id="jobRepository"
  class="org.springframework.batch.core.repository.support.MapJobRepositoryFactoryBean">
    <property name="transactionManager" ref="transactionManager"/>
</bean>

请注意,in-memory repository 是易失性的,因此不允许在 JVM 实例之间重新启动。它也不能保证同时启动两个具有相同参数的 job 实例,并且不适合在 multi-threaded Job 或本地分区的 Step 中使用。因此,只要您需要 features,就可以使用 repository 的数据库 version。

但是它确实需要定义 transaction manager,因为 repository 中存在回滚语义,并且因为业务逻辑可能仍然是 transactional(e.g. RDBMS 访问)。出于测试目的,许多人发现ResourcelessTransactionManager很有用。

4.3.4 Non-standard Repository 中的数据库类型

如果您使用的数据库平台不在受支持的平台列表中,那么如果 SQL 变量足够接近,您可以使用其中一种受支持的类型。为此,您可以使用原始JobRepositoryFactoryBean而不是命名空间快捷方式,并使用它将数据库类型设置为最接近的 match:

<bean id="jobRepository" class="org...JobRepositoryFactoryBean">
    <property name="databaseType" value="db2"/>
    <property name="dataSource" ref="dataSource"/>
</bean>

(如果不是 specified.),JobRepositoryFactoryBean会尝试 auto-detect 数据库类型 auto-detect 平台之间的主要区别主要是通过递增主键的策略来解决的,所以通常也可能需要覆盖incrementerFactory(使用其中一个)来自 Spring Framework 的标准 implementations)。

如果即使这不起作用,或者你没有使用 RDBMS,那么唯一的选择可能是实现SimpleJobRepository所依赖的各种Dao接口,并以正常的 Spring 方式手动连接一个。

4.4 配置 JobLauncher

JobLauncher接口最基本的 implementation 是SimpleJobLauncher。它唯一需要的依赖是JobRepository,在 order 中获取执行:

<bean id="jobLauncher"
      class="org.springframework.batch.core.launch.support.SimpleJobLauncher">
    <property name="jobRepository" ref="jobRepository" />
</bean>

一旦获得JobExecution,它将被传递给Job的 execute 方法,最终将JobExecution返回给调用者:

序列很简单,从调度程序启动时效果很好。但是,尝试从 HTTP 请求启动时会出现问题。在这种情况下,启动需要异步完成,以便SimpleJobLauncher立即返回其调用者。这是因为保持 HTTP 请求在 long running 进程(如批处理)所需的 time 时间内打开是不好的做法。 示例序列如下:

可以通过配置TaskExecutor轻松配置SimpleJobLauncher以允许此方案:

<bean id="jobLauncher"
      class="org.springframework.batch.core.launch.support.SimpleJobLauncher">
    <property name="jobRepository" ref="jobRepository" />
    <property name="taskExecutor">
        <bean class="org.springframework.core.task.SimpleAsyncTaskExecutor" />
    </property>
</bean>

spring TaskExecutor接口的任何 implementation 都可用于控制作业异步执行的方式。

4.5 跑一个 Job

至少,启动批处理 job 需要两件事:Job要启动,JobLauncherJobLauncher。两者都可以包含在相同的 context 或不同的上下文中。例如,如果从 line 命令启动 job,将为每个 Job 实例化一个新的 JVM,因此每个 job 都有自己的JobLauncher。但是,如果在HttpRequest范围内的 web 容器内运行,通常会有一个JobLauncher,配置为异步 job 启动,多个请求将调用以启动其作业。

4.5.1 从命令 Line 中运行作业

对于想要从企业调度程序运行其作业的用户,命令 line 是主要接口。这是因为大多数调度程序(使用 Quartz 的 exception 除非使用NativeJob)直接与操作系统进程一起工作,主要是使用 shell 脚本启动。除了 shell 脚本之外,还有许多方法可以启动 Java process,例如 Perl,Ruby,甚至是 build 工具,例如 ant 或 maven。但是,由于大多数人都熟悉 shell 脚本,因此这个示例将重点关注它们。

CommandLineJobRunner

因为启动 job 的脚本必须启动 Java 虚拟机,所以需要一个带有 main 方法的 class 作为主要入口点。 Spring Batch 提供了一个 implementation 来实现这个目的:CommandLineJobRunner。重要的是要注意,这只是引导 application 的一种方法,但是有许多方法可以启动 Java process,而且这个 class 绝不应该被视为确定的。 CommandLineJobRunner执行四项任务:

  • 加载适当的ApplicationContext

  • 将命令 line arguments 解析为JobParameters

  • 根据 arguments 找到合适的 job

  • 使用 application context 中提供的JobLauncher来启动 job。

所有这些任务都只使用传入的 arguments 来完成。以下是必需的 arguments:

表格 1_.CommandLineJobRunner arguments

jobPath将用于创建ApplicationContext的 XML 文件的位置。此文件应包含 run 完整Job所需的一切
作业名job 的 name 是 run。

这些 arguments 必须首先传入路径,然后传递 name。之后的所有 arguments 都被视为 JobParameters,并且必须采用'name=value'的格式:

bash$ java CommandLineJobRunner endOfDayJob.xml endOfDay schedule.date(date)=2007/05/05

在大多数情况下,您可能希望使用清单在 jar 中声明主 class,但为简单起见,直接使用了 class。此 example 使用与domain 部分相同的'EndOfDay'example。第一个参数是'endOfDayJob.xml',它是包含Job的 Spring ApplicationContext。第二个参数'endOfDay'代表 job name。最后一个参数'schedule.date(date)=2007/05/05'将转换为JobParameters。 XML configuration 的一个例子如下:

<job id="endOfDay">
    <step id="step1" parent="simpleStep" />
</job>

<!-- Launcher details removed for clarity -->
<beans:bean id="jobLauncher"
         class="org.springframework.batch.core.launch.support.SimpleJobLauncher" />

这个例子过于简单了,因为对_循环_Batch 中的批 job 提出了更多的要求,但它有助于显示CommandLineJobRunner的两个主要要求:JobJobLauncher

ExitCodes

从 command-line 启动批处理 job 时,通常使用企业调度程序。大多数调度程序都相当愚蠢,只能在 process level 上工作。这意味着他们只知道某些操作系统 process,例如他们正在调用的 shell 脚本。在这种情况下,与 job 成功或失败通信的唯一方法是通过 return 代码。 return code 是 process 返回给调度程序的数字,表示 run 的结果。在最简单的情况下:0 表示成功,1 表示失败。但是,可能有更复杂的场景:如果 job A 返回 4 启动 job B,如果它返回 5 启动 job C.这种类型的行为是在调度程序 level 配置的,但重要的是处理 framework 如 Spring Batch 提供了一种方法 return 特定批 job 的'Exit Code'的数字表示。在 Spring Batch 中,这封装在ExitStatus中,在第 5 章中有更详细的介绍。为了讨论退出代码,唯一需要知道的是ExitStatus有一个由 framework 设置的 exit code property(或者开发人员)并作为从JobLauncher返回的JobExecution的一部分返回。 CommandLineJobRunner使用ExitCodeMapper接口将此 string value 转换为数字:

public interface ExitCodeMapper {

    public int intValue(String exitCode);

}

ExitCodeMapper的基本 contract 是,给定一个 string exit code,将返回一个数字表示。 job 运行器使用的默认 implementation 是 SimpleJvmExitCodeMapper,它返回 0 表示完成,1 表示一般错误,2 表示任何 job 运行程序错误,例如无法在提供的 context 中找到Job。如果需要比上面 3 个值更复杂的东西,则必须提供ExitCodeMapper接口的自定义 implementation。因为CommandLineJobRunner是创建ApplicationContext的 class,因此无法“连接在一起”,所以任何需要覆盖的值都必须自动装配。这意味着如果在 BeanFactory 中找到ExitCodeMapper的_i实现,则会在创建 context 后将其注入到运行器中。为提供自己的ExitCodeMapper而需要做的就是将 implementation 声明为 root level bean,并确保它是运行器加载的ApplicationContext的一部分。

4.5.2 从 Web 容器中运行作业

从历史上看,如上所述,已从 command-line 启动了批处理作业等离线处理。但是,在很多情况下,从HttpRequest启动是更好的选择。许多此类用例包括报告,ad-hoc job running 和 web application 支持。因为批量 job 的定义是 long running,所以最重要的问题是确保异步启动 job:

在这种情况下,控制器是一个 Spring MVC 控制器。有关 Spring MVC 的更多信息,请访问:http://docs.spring.io/spring/docs/3.2.x/spring-framework-reference/html/mvc.html。控制器使用已配置为启动异步JobLauncher启动Job,立即返回JobExecutionJob可能仍然是 running,但是,这种非阻塞行为允许控制器立即 return,这在处理HttpRequest时是必需的。 示例如下:

@Controller
public class JobLauncherController {

    @Autowired
    JobLauncher jobLauncher;

    @Autowired
    Job job;

    @RequestMapping("/jobLauncher.html")
    public void handle() throws Exception{
        jobLauncher.run(job, new JobParameters());
    }
}

4.6 高级 Meta-Data 用法

到目前为止,已经讨论了 JobLauncher 和 JobRepository 接口。它们一起表示简单启动 job,以及批处理 domain objects 的基本 CRUD 操作:

JobLauncher使用JobRepository创建新的JobExecution objects 并运行它们。 和Step __mplempleations 稍后在运行期间使用相同的JobRepository进行相同执行的基本更新。基本操作足以满足简单场景,但在具有数百个批处理作业和复杂调度要求的大型批处理环境中,需要更高级的元数据访问:

将在下面讨论的JobExplorerJobOperator接口添加了用于查询和控制元数据的附加功能。

4.6.1 查询 Repository

任何高级 features 之前的最基本需求是能够在 repository 中查询现有的执行。此功能由JobExplorer接口提供:

public interface JobExplorer {

    List<JobInstance> getJobInstances(String jobName, int start, int count);

    JobExecution getJobExecution(Long executionId);

    StepExecution getStepExecution(Long jobExecutionId, Long stepExecutionId);

    JobInstance getJobInstance(Long instanceId);

    List<JobExecution> getJobExecutions(JobInstance jobInstance);

    Set<JobExecution> findRunningJobExecutions(String jobName);
}

从上面的方法签名可以看出,JobExplorerJobRepository的 read-only version,和JobRepository一样,它可以通过工厂 bean 轻松配置:

<bean id="jobExplorer" class="org.spr...JobExplorerFactoryBean"
      p:dataSource-ref="dataSource" />

本章前面,有人提到可以修改JobRepository的 table 前缀以允许不同的版本或模式。因为JobExplorer正在使用相同的表,所以它也需要能够设置前缀:

<bean id="jobExplorer" class="org.spr...JobExplorerFactoryBean"
      p:dataSource-ref="dataSource" p:tablePrefix="BATCH_" />

4.6.2 JobRegistry

JobRegistry(及其 parent 接口 JobLocator)不是必需的,但如果要跟踪 context 中可用的作业,它可能很有用。在其他地方(e.g. 在 child 上下文中)创建作业时,它也可以在 application context 中集中收集作业。自定义 JobRegistry implementations 还可用于操作已注册作业的名称和其他 properties。 framework 只提供了一个 implementation,它基于 job name 到 job 实例的简单 map。它的配置简单如下:

<bean id="jobRegistry" class="org.spr...MapJobRegistry" />

有两种方法可以自动填充 JobRegistry:使用 bean post 处理器和使用 registrar 生命周期 component。以下各节介绍了这两种机制。

JobRegistryBeanPostProcessor

这是一个 bean post-processor,它可以在创建时注册所有作业:

<bean id="jobRegistryBeanPostProcessor" class="org.spr...JobRegistryBeanPostProcessor">
    <property name="jobRegistry" ref="jobRegistry"/>
</bean>

虽然并非严格必要,但 example 中的 post-processor 已被赋予 id,因此它可以包含在 child 上下文中(e.g. 作为 parent bean 定义),并导致在此创建的所有作业也自动被注册。

AutomaticJobRegistrar

这是一个生命周期 component,它创建 child 上下文并在创建时从这些上下文中注册作业。这样做的一个优点是,虽然 child 上下文中的 job 名称仍然必须在注册表中是全局唯一的,但它们的依赖项可以具有“自然”名称。因此,对于 example,您可以创建一组 XML configuration files,每个文件只有一个Job,但所有ItemReader的定义都与“读者”。如果所有这些 files 都被导入到同一个 context 中,则 reader 定义会发生冲突并相互覆盖,但是使用自动注册器会避免这种情况。这样可以更轻松地集成由 application 的单独模块提供的作业。

<bean class="org.spr...AutomaticJobRegistrar">
   <property name="applicationContextFactories">
      <bean class="org.spr...ClasspathXmlApplicationContextsFactoryBean">
         <property name="resources" value="classpath*:/config/job*.xml" />
      </bean>
   </property>
   <property name="jobLoader">
      <bean class="org.spr...DefaultJobLoader">
         <property name="jobRegistry" ref="jobRegistry" />
      </bean>
   </property>
</bean>

注册商有两个必需的 properties,一个是ApplicationContextFactory的 array(这里是从一个方便的工厂 bean 创建的),另一个是JobLoaderJobLoader负责管理 child 上下文的生命周期并在JobRegistry中注册作业。

ApplicationContextFactory负责 creating child context,而 common 的最常用用法是使用ClassPathXmlApplicationContextFactory。这个工厂的一个特点就是默认情况下它会将一些 configuration 从 parent context 复制到 child。因此,例如,如果它应该与 parent 相同,则不必在 child 中PropertyPlaceholderConfigurer或 AOP configuration。

如果需要,AutomaticJobRegistrar可以与JobRegistryBeanPostProcessor一起使用(因为DefaultJobLoader也使用 long)。例如,如果在主 parent context 和 child 位置中定义了作业,则可能需要这样做。

4.6.3 JobOperator

如前所述,JobRepository在 meta-data 上提供 CRUD 操作,而JobExplorer在 meta-data 上提供 read-only 操作。但是,这些操作在一起使用以执行 common 监视任务时非常有用,例如停止,重新启动或汇总 Job,这通常由批处理操作员完成。 Spring Batch 通过JobOperator接口提供这些类型的操作:

public interface JobOperator {

    List<Long> getExecutions(long instanceId) throws NoSuchJobInstanceException;

    List<Long> getJobInstances(String jobName, int start, int count)
          throws NoSuchJobException;

    Set<Long> getRunningExecutions(String jobName) throws NoSuchJobException;

    String getParameters(long executionId) throws NoSuchJobExecutionException;

    Long start(String jobName, String parameters)
          throws NoSuchJobException, JobInstanceAlreadyExistsException;

    Long restart(long executionId)
          throws JobInstanceAlreadyCompleteException, NoSuchJobExecutionException,
                  NoSuchJobException, JobRestartException;

    Long startNextInstance(String jobName)
          throws NoSuchJobException, JobParametersNotFoundException, JobRestartException,
                 JobExecutionAlreadyRunningException, JobInstanceAlreadyCompleteException;

    boolean stop(long executionId)
          throws NoSuchJobExecutionException, JobExecutionNotRunningException;

    String getSummary(long executionId) throws NoSuchJobExecutionException;

    Map<Long, String> getStepExecutionSummaries(long executionId)
          throws NoSuchJobExecutionException;

    Set<String> getJobNames();

}

上述操作表示来自许多不同接口的方法,例如JobLauncherJobRepositoryJobExplorerJobRegistry。因此,JobOperatorSimpleJobOperator提供的 implementation 具有许多依赖项:

<bean id="jobOperator" class="org.spr...SimpleJobOperator">
    <property name="jobExplorer">
        <bean class="org.spr...JobExplorerFactoryBean">
            <property name="dataSource" ref="dataSource" />
        </bean>
    </property>
    <property name="jobRepository" ref="jobRepository" />
    <property name="jobRegistry" ref="jobRegistry" />
    <property name="jobLauncher" ref="jobLauncher" />
</bean>

如果在 job repository 上设置 table 前缀,请不要忘记在 job 资源管理器上设置它。

4.6.4 JobParametersIncrementer

JobOperator上的大多数方法都是 self-explanatory,更详细的解释可以在接口的 javadoc上找到。但是,startNextInstance方法值得注意。此方法将始终启动Job的新实例。如果JobExecution中存在严重问题且需要从头开始重新开始Job,这可能非常有用。与JobLauncher不同,它需要一个新的JobParameters object,如果参数不同于任何先前的参数集,它将触发一个新的JobInstancestartNextInstance方法将使用JobParametersIncrementer绑定到Job来强制Job到一个新实例:

public interface JobParametersIncrementer {

    JobParameters getNext(JobParameters parameters);

}

JobParametersIncrementer的 contract 是,给定一个JobParameters object,它将通过递增它可能包含的任何必要值来_return'next'JobParameters object。这个策略很有用,因为 framework 无法知道JobParameters的哪些更改使其成为“下一个”实例。对于 example,如果JobParameters中唯一的 value 是 date,并且应该创建下一个实例,那么 value 应该增加一天吗?或者一周(如果 job 是每周一次)?对于有助于识别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();
    }
}

在这个 example 中,带有 key 为'run.id'的 value 用于区分JobInstances。如果传入的JobParameters为 null,则可以假定Job之前从未 run,因此可以返回其初始 state。但是,如果不是,则获取旧的 value,递增 1 并返回。增量器可以通过命名空间中的'incrementer'属性与Job相关联:

<job id="footballJob" incrementer="sampleIncrementer">
    ...
</job>

4.6.5 停止 Job

JobOperator最常见的用例之一是优雅地停止Job:

Set<Long> executions = jobOperator.getRunningExecutions("sampleJob");
jobOperator.stop(executions.iterator().next());

关闭不是立即关闭的,因为没有办法强制立即关闭,特别是如果执行当前在 framework 无法控制的开发人员 code 中,例如业务服务。但是,只要控制权返回到 framework,它就会将当前StepExecution的状态设置为BatchStatus.STOPPED,保存它,然后在完成之前对JobExecution执行相同操作。

4.6.6 中止 Job

可以重新启动_j的 job 执行(如果 Job 可以重新启动)。 _frame不会重新启动状态为ABANDONED的 job 执行。 ABANDONED状态也用于 step 执行,以在重新启动的 job 执行中将它们标记为可跳过:如果 job 正在执行并遇到在前一个失败的 job 执行中标记为ABANDONED的 step,它将继续执行到下一个 step(由 job 流定义和 step 执行退出状态确定)。

如果 process 死了("kill -9"或服务器失败)job 当然不是 running,但是 JobRepository 无法知道,因为 no-one 在 process 死之前告诉它。您必须手动告诉它您知道执行失败或应该被视为中止(将其状态更改为FAILEDABANDONED) - 这是一个业务决策,并且无法自动执行。如果状态不可重新启动,或者您知道重启数据有效,则仅将状态更改为FAILED。 Spring Batch Admin JobService中有一个实用程序可以中止 job 执行。

Updated at: 9 months ago
3.9. 批处理命名空间Table of content5. 配置 Step