spring batch之三 配置和运行Job

来源:互联网 发布:mac的解压rar软件 编辑:程序博客网 时间:2024/06/12 21:35

本文基于spring batch reference 第四章 

Configuring and Running a Job

在spring batch之一 域模型中我们讨论了spring batch 的原型.

4.1. 配置一个job

配置一个job,只需要三个必要的依赖: 一个名字,JobRepository , 和一列steps.

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

上面的例子使用一个父Bean定义去创建step.Xml命名空间默认引用一个id 是'jobRepository' job repository。'jobRepository' 大小区分大小写。 然而,我们也可以明确定义job 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>

4.1.1. 重启能力

当一个job不能被restart的时候,只需要将 restartable 属性设置成 'false'.一个job instance和job parameter相关,每次在启动job时传入的参数的值相同,可以认为同一个job instance. 我们可以将运行job的当前时间作为一个参数传入,这样子每次启动都是不同的job instance.

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

启动一个restartable=false的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}

上面的测试代码段显示第一次创建一个restartable=false 的job的JobExecution是没有问题的. 第二次将会抛出JobRestartException.

4.1.2. 解析job execution

我们可以实现job listener接口,在job运行前和运行后加入自己的一些逻辑.

public interface JobExecutionListener {    void beforeJob(JobExecution jobExecution);    void afterJob(JobExecution jobExecution);}

JobListeners can be added to a SimpleJob via the listeners element on the job:

<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获取job的结束状态.

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

对应这个接口的 annotations :

  • @BeforeJob

  • @AfterJob

4.1.3. 从一个父job中继承

如果一组jobs分享一些类似的但不同的配置.我们可以定义一个父job.类似于class的继承,子job将会结合自身的和父job的元素和属性.

下面的例子, "baseJob"是一个抽象的 Job 定义,只包含了一个listen 列表. Job "job1" 从"baseJob"中继承了listener列表并和自己的listener列表合并到一起.

<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>

4.1.4. job 参数验证.

可以配置一个job parameter validator <validator ref="paremetersValidator"/>验证job parameter的正确性. 有个一个默认的实现: DefaultJobParametersValidator,我们可以参考或者使用默认实现验证job parameter. 

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

4.2. 配置 JobRepository

job repository 保存spring batch job的运行信息,对spring batch的自带表进行基本的CRUD操作。 请参考下面的配置: 配置数据源,事务管理器,事务隔离级别,table prefix(可以使用默认).

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


4.3.1. job repository的事务配置

如果使用了命名空间(batch自身的命名空间), 会自动创建事务性的advice. 这能确保元数据的正确性. 默认的事务的隔离级别对create* 方法是SERIALIZABLE,非常严格: READ_COMMITTED 就应该工作的很好; READ_UNCOMMITTED 也可以,如果不是同时启动一个job. 因为调用create*非常短 t,  SERIALIZED 不太可能会产生问题, 只要数据库平台支持SERIALIZED 事务级别. 我们也可以重写: 

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

使用aop命名空间配置:

<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>

4.3.2. 改变table 前缀

默认的spring batch 自带的表都以BATCH_开头,当然我们可以改变这个前缀. table-prefix配置的前缀必须和database中的表的前缀相同.

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

4.3.3. 在内存中的Job Repository

spring提供了一个内存版本的job repository. 在做一些测试或者不需要保存job 运行状态的情况下,我们使用内存版的job repository.

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

内存版的job repository因为不能保存job的运行信息,有很多缺陷,在很多高级应用-跨JVM(跨机器)中不能使用.

4.4. 配置一个 JobLauncher

最简单的JobLauncher 接口的实现是 SimpleJobLauncher. 他只需要一个 JobRepository:

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

一旦获得 JobExecution ,JobExecution将会被传入job的执行方法. 最终返回 JobExecution 给调用者.

一个同步调用的顺序图. 我们可以通过一个scheduler调用spring batch.尽量不要使用http,因为batch运行时间长,会阻塞连接.

我们可以配置一个TaskExecutor实现Sim上图的异步调用:

<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>

4.5. 运行job

运行job最少的条件需要一个job和一个job launcher. 

4.5.1. 从命令行运行job

调度系统中最常用的一种方式,例如使用quartz,或使用shell脚本.

4.5.1.1. The CommandLineJobRunner

 spring 提供了一个实现类CommandLineJobRunner.这仅仅是一种从command line启动spring batch的方法.  类CommandLineJobRunner 执行了四个任务:

  • 装载 ApplicationContext

  • 传递line command 参数给JobParameters

  • 根据参数定位具体的job

  • 使用JobLauncher运行job.

所有的这些任务都是通过传递的参数完成. 下面是必须的参数:

Table 4.1. CommandLineJobRunner 参数

jobPathspring application context 的xml文件路径jobNamejob 的名称

jobPath jobName 必须作为第一个和第二个参数.作为job paramete的参数必须以'name=value'的格式传递.

bash$ java CommandLineJobRunner endOfDayJob.xml endOfDay schedule.date(date)=2007/05/05
<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" />

4.5.1.2. 退出代码

一个ExitStatus将会作为JobExecution的一部分从JobLauncher返回.CommandLineJobRunner使用一个ExitCodeMapper接口将string转换为int类型:

public interface ExitCodeMapper {    public int intValue(String exitCode);}

ExitCodeMapper 的默认实现是 SimpleJvmExitCodeMapper,0 代表结束, 1 代表通用错误, 2  代表job调用者错误. 

4.5.2. 在web容器中运行job

通常在web容器中以http的方式调用spring batch job,spring batch job都是以异步的方式执行.

controller是一个spring mvc 的control.. controller 使用JobLauncher 异步启动一个job, JobLauncher立即返回JobExecution.Job 会继续运行。 

@Controllerpublic class JobLauncherController {    @Autowired    JobLauncher jobLauncher;    @Autowired    Job job;    @RequestMapping("/jobLauncher.html")    public void handle() throws Exception{        jobLauncher.run(job, new JobParameters());    }}

4.6. 高级的元数据的使用

到现在为止,已经讨论过JobLauncher 和 JobRepository. 

JobLauncher 使用 JobRepository 去创建新的 JobExecution 对象并运行. 在job中后续的Job 和 Step 的实现使用同样的JobRepository 作为基本的CRUD操作. 基本的操作在一些简单的场景中足够了,但是在许多batch job的复杂调度环境中,访问元数据是必须的:

4.6.1. Querying the Repository

在任何高级的特性之前,最基本的功能室查询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);}

从上面的方法的签名中可以得知, JobExplorer 是 JobRepository的一个只读的版本, 像 JobRepository, 它可以很简单的通过一个工厂bean配置:

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

带table prefix的配置:

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

4.6.2. JobRegistry

JobRegistry 不是必须的, 但是它可以帮助你了解有多少job在spring context中.  spring framework提供了一个唯一的实现,配置如下:

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

有两中方法自动填充job mapping,一种是使用一个spring bean post processor,另一种使用一种注册的有生命周期的组件:

4.6.2.1. JobRegistryBeanPostProcessor

这是一个 bean post-processor 可以注册所有的以创建的job:

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

4.6.2.2. AutomaticJobRegistrar

这是一个生命周期组件,创建子contexts并从这些子contexts中注册job。

<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>

注册器需要两个必要的属性,一个是 ApplicationContextFactory 数组,另外一个是JobLoader.JobLoader 负责管理子contexts的生命周期,并注在obRegistry中注册job.

ApplicationContextFactory 负责创建子 context,最常用的是使用 ClassPathXmlApplicationContextFactory. 一个特性之一是它会从父context中拷贝一些配置到子context中.所以不需要再子context中重新定义PropertyPlaceholderConfigurer 或者 AOP 配置,他会继承父context.

4.6.3. JobOperator

 sping batch 提供了JobOperator对batch 操作进行重启,总结,停止操作.

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();}

上面的操作代表了来自不同接口的方法, 比如JobLauncherJobRepositoryJobExplorer, 和 JobRegistry. 基于这个原因,JobOperator的默认实现SimpleJobOperator有很多依赖:

<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>

4.6.4. JobParametersIncrementer

JobOperator 中的大多数方法是自解释的. 但是 startNextInstance 总是启动一个新的 Job实例. 如果当一个job在开始阶段发生了严重的错误,需要再次重启的时候,这个方法非常有用. 不像JobLauncher需要一些不同的job 参数才能重启一个job实例. startNextInstance 方法将使用 JobParametersIncrementer 试图强制重启一个新的实例:

public interface JobParametersIncrementer {    JobParameters getNext(JobParameters parameters);}

JobParametersIncrementer 的含义是给定一个JobParameters 对象通过增加一些必要的值将返回下一个JobParameters 对象。 参考下面的一个例子:

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();    }}
<job id="footballJob" incrementer="sampleIncrementer">    ...</job>

4.6.5. 停止一个job

大多数情况下使用JobOperator 停止一个job:

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

这段代码不能立即停止job运行,当业务逻辑把控制权返回给spring batch framework的时候,他会设置 StepExecution 的状态为 BatchStatus.STOPPED, 保存它, 然后对 JobExecution 。

4.6.6. Aborting a Job

状态为 FAILED的job 可以被重启. 状态为ABANDONED 的不能重启. The ABANDONED 也被用在step executions 标记setp execution是可以跳过的,在一个重启的job中. 如果一个job碰到一个step 在上次的执行中被标记为ABANDONED ,它将跳到下一步.

如果线程死了 ("kill -9" 或者其他错误),job 实例不能知道他的运行状态.你必须手动的告诉他失败了或者aborted. 把他的状态修改为 FAILED 或者 ABANDONED . 如果job不能被重启的话,就把他的状态修改为 FAILED


0 0
原创粉丝点击