1.spring batch 轻量级的,完全面向spring的批处理框架,可以应用于企业级大量的数据处理系统。
2.数据量大、系统根据配置自动完成,无需人工干预、定期执行。
3.应用可以拆分为3个环节:读数据、数据处理、写数据
4.spring batch是一款批处理框架,需要结合调度框架使用。
5.spring-batch-core:批处理领域相关类;spring-batch-infrastructure:基础访问处理框架。
6.所有读操作均需要实现ItemReader接口,支持基于数据库游标和分页两类操作;
7.所有写操作均需要实现ItemWriter接口,该接口只有一个write方法,参数时处理的结果列表。
8.业务处理均需要显示ItemProcesser接口,用于完成相应业务处理。
9.spring batch将批处理任务称为一个job,job下分为多个step;step是一个独立的顺序处理步骤;多个批处理step按照一定的流程组成一个job。
10.step的配置包含:读数据、处理数据、写数据相关的bean,还需要指定commit-interval:事务提交粒度,表名多少条处理完成后,提交一次事务;还需要指定chunk-completion-policy:完成策略,即当发生什么情况时表明该step已经完成,可以转入后续处理,默认策略是读入数据为空时认为step结束。
11.step配置范例:
<job id="messageJob"> <step id="messageStep"> <tasklet> <chunk reader="messageReader" processor="messageProcessor" writer="messageWriter" commit-interval="5" chunk-completion-policy=""> </chunk> </tasklet> </step> </job>
12.JobRepository用于对job进行管理,并需要为其指定一个事务管理器。
<beans:bean id="jobRepository" class="org.springframework.batch.core.repository.support.MapJobRepositoryFactoryBean"> <beans:property name="transactionManager" ref="transactionManager" /> </beans:bean> <beans:bean id="transactionManager" class="org.springframework.batch.support.transaction.ResourcelessTransactionManager"/>
13.spring batch提供了JobLauncher接口用于运行Job,我们需要为JobLauncher指定一个JobRepository,指定一个任务执行器,最后通过run方法来执行job,该方法需要指定执行的job参数以及执行参数:JobParameters。
public class Main { public static void main(String[] args) { ClassPathXmlApplicationContext c = new ClassPathXmlApplicationContext("message_job.xml"); SimpleJobLauncher launcher = new SimpleJobLauncher(); launcher.setJobRepository((JobRepository) c.getBean("jobRepository")); launcher.setTaskExecutor(new SimpleAsyncTaskExecutor()); try { launcher.run((Job) c.getBean("messageJob"), new JobParameters()); } catch (Exception e) { e.printStackTrace(); } } }
14.对于srping batch来说,JobParameters相同的任务只能成功运行一次。
Map<String,JobParameter> parameters = new HashMap<String,JobParameter>(); parameters.put(RUN_MONTH_KEY,new JobParameter("2011-10")); launcher.run((Job) c.getBean("messageJob"),new JobParameters(parameters)); parameters.put(RUN_MONTH_KEY,new JobParameter("2011-11")); launcher.run((Job) c.getBean("messageJob"),new JobParameters(parameters));
15.spring batch中可以通过配置实现步骤step的重试:
<job id="messageJob" restartable="true"> <step id="messageStep"> <tasklet> <chunk reader="messageReader" processor="messageProcessor" writer="messageWriter" commit-interval="5" chunk-completion-policy="" retry-limit="2"> <retryable-exception-classes> <include class="java.lang.RuntimeException" /> </retryable-exception-classes> </chunk> </tasklet> </step> </job>
其中:retry-limit为重试次数;retryable-exception-classes为捕获到对应的异常进行重试,也可以配置exclude。
16.任务重试:
spring batch从出错的事务边界内第一条记录重复执行,这样确保了事务的完整性
Map<String,JobParameter> parameters = new HashMap<String,JobParameter>(); parameters.put(RUN_MONTH_KEY,new JobParameter("2011-10")); launcher.run((Job) c.getBean("messageJob"),new JobParameters(parameters)); Thread.sleep(10000); launcher.run((Job) c.getBean("messageJob"),new JobParameters(parameters));
17.运行时管理:
JobInstance | 该类记录了 Job 的运行实例。举例:10 月和 11 月分别执行同一 Job,将生成两个 JobInstance。主要信息有:标识、版本、Job 名称、Job 参数 |
JobExecution | 该类记录了 Job 的运行记录。如上面的示例,Job 第一次运行失败,第二次运行成功,那么将形成两条运行记录,但是对应的是同一个运行实例。主要信息有:Job 的运行时间、运行状态等。 |
JobParameters | 该类记录了 Job 的运行参数 |
ExecutionContext | 该类主要用于开发人员存储任务运行过程中的相关信息(以键值对形式),主要分为 Job 和 Step 两个范围 |
StepExecution | 该类与 JobExecution 类似,主要记录了 Step 的运行记录。包括此次运行读取记录条数、输出记录条数、提交次数、回滚次数、读跳过条数、处理跳过条数、写跳过条数等信息 |
18.step flow
一个Job顺序执行Job中各step,通过配置step的next属性,指定下一个步骤,
19.条件流程
spring batch通过设置step元素的on属性来支持条件流程,on属性取值为step的结束状态,特殊指定一种结束状态时,需要为step添加一个监听器,以返回指定的结束状态。
<step id="payStep"> <tasklet> <chunk reader="billDbReader" processor="payProcessor" writer="payDbWriter" commit-interval="5" chunk-completion-policy="" skip-limit="100"> <skippable-exception-classes> <include class="org.springframework.batch.sample.MoneyNotEnoughException" /> </skippable-exception-classes> </chunk> </tasklet> <next on="COMPLETED WITH SKIPS" to="messageStep"/> <listeners> <listener ref="payStepCheckingListener"></listener> </listeners> </step>
public class PayStepCheckingListener extends StepExecutionListenerSupport { @Override public ExitStatus afterStep(StepExecution stepExecution) { String exitCode = stepExecution.getExitStatus().getExitCode(); if (!exitCode.equals(ExitStatus.FAILED.getExitCode()) && stepExecution.getSkipCount() > 0) { return new ExitStatus("COMPLETED WITH SKIPS"); } else { return null; } } }
20.流程决策
step的结束状态不足于满足较为复杂的条件流程,流程决策器结合JobExecution和StepExecution,需要实现:JobExecutionDecider接口
public class MessagesDecider implements JobExecutionDecider { public FlowExecutionStatus decide(JobExecution jobExecution,StepExecution stepExecution) { String exitCode = stepExecution.getExitStatus().getExitCode(); if (!exitCode.equals(ExitStatus.FAILED.getExitCode()) && stepExecution.getSkipCount() > 0) { return new FlowExecutionStatus("COMPLETED WITH SKIPS"); } else { return FlowExecutionStatus.COMPLETED; } } }
<job id="billingJob" restartable="true"> <step id="billingStep" next="payStep"></step> <step id="payStep" next="decider"></step> <decision id="decider" decider="messagesDecider"> <next on="COMPLETED WITH SKIPS" to="messageStep" /> <end on="COMPLETED" /> </decision> <step id="messageStep"></step> </job>
21.step内并发处理
通过设置step的task-executor属性,可以让当前的step以并发方式执行,通过throttle-limit设置并发线程数。
<step id="billingStep" next="payStep"> <tasklet task-executor="taskExecutor" throttle-limit="5"> <chunk reader="userDbReader" processor="billingProcessor" writer="billDbWriter" commit-interval="5" chunk-completion-policy=""> </chunk> </tasklet> </step> <beans:bean id="taskExecutor" class="org.springframework.core.task.SimpleAsyncTaskExecutor"> </beans:bean>
22.主要任务执行器
SyncTaskExecutor | 简单同步执行器 | 否 |
ThrottledTaskExecutor | 该执行器为其他任意执行器的装饰类,并完成提供执行次数限制的功能 | 视被装饰的执行器而定 |
SimpleAsyncTaskExecutor | 简单异步执行器,提供了一种最基本的异步执行实现 | 是 |
WorkManagerTaskExecutor | 该类作为通过 JCA 规范进行任务执行的实现,其包含 JBossWorkManagerTaskExecutor 和 GlassFishWorkManagerTaskExecutor 两个子类 | 是 |
ThreadPoolTaskExecutor | 线程池任务执行器 | 是 |
23.step间并发处理,split flow
<job id="billingJob" restartable="true"> <step id="billingStep" next="splitStep"> <tasklet task-executor="taskExecutor" throttle-limit="5"> <chunk reader="userDbReader" processor="billingProcessor" writer="billDbWriter" commit-interval="5" chunk-completion-policy=""> </chunk> </tasklet> </step> <split id="splitStep" task-executor="taskExecutor" next="decider"> <flow> <step id="billingMessageStep"> <tasklet> <chunk reader="billDbReader" processor="billMessageItemProcessor" writer="messageDbWriter" commit-interval="5" chunk-completion-policy=""> </chunk> </tasklet> </step> </flow> <flow> <step id="payStep"> <tasklet> <chunk reader="billDbReader" processor="payProcessor" writer="payDbWriter" commit-interval="5" chunk-completion-policy="" skip-limit="100"> <skippable-exception-classes> <include class="org.springframework.batch.sample.MoneyNotEnoughException" /> </skippable-exception-classes> </chunk> </tasklet> </step> </flow> </split> <decision id="decider" decider="messagesDecider"> <next on="COMPLETED WITH SKIPS" to="paymentMessageStep" /> <end on="COMPLETED" /> </decision> <step id="paymentMessageStep"> <tasklet> <chunk reader="billArrearsDbReader" processor="messageProcessor" writer="messageDbWriter" commit-interval="5" chunk-completion-policy=""> </chunk> </tasklet> </step> </job>
注意:只有当split中所有step执行完毕之后,才会执行split元素的下一步,同时需要为split元素制定task-executor为并行执行器,默认为:SyncTaskExecutor,同步执行器。
24.partition step,分区step
可以将耗时的步骤分配到多个机器上执行。
<beans:bean name="step" class="org.springframework.batch.core.partition.support.PartitionStep"> <beans:property name="partitionHandler"> <beans:bean class="org.springframework.batch.core.partition.support.TaskExecutorPartitionHandler"> <beans:property name="step" ref="remoteStep" /> <beans:property name="gridSize" value="10" /> <beans:property name="taskExecutor" ref="taskExecutor" /> </beans:bean> </beans:property> <beans:property name="stepExecutionSplitter"> <beans:bean class="org.springframework.batch.core.partition.support.SimpleStepExecutionSplitter"> <beans:constructor-arg ref="jobRepository" /> <beans:constructor-arg ref="messageStep" /> <beans:constructor-arg ref="simplePartitioner" /> </beans:bean> </beans:property> <beans:property name="jobRepository" ref="jobRepository" /> </beans:bean> <step id="messageStep"> <tasklet task-executor="taskExecutor"> <chunk reader="messageReader" processor="messageProcessor" writer="messageWriter" commit-interval="5" chunk-completion-policy="" retry-limit="2"> <retryable-exception-classes> <include class="java.lang.RuntimeException" /> </retryable-exception-classes> </chunk> </tasklet> </step> <beans:bean id="remoteStep" class="org.springframework.remoting.httpinvoker.HttpInvokerProxyFactoryBean"> <beans:property name="serviceInterface" value="org.springframework.batch.core.Step" /> <beans:property name="serviceUrl" value="${batch.remote.base.url}/steps/messageStep" /> </beans:bean>