202208-源码解析springbatch的job是如何运行的?
注,本文中的demo代码节选于图书《Spring Batch批处理框架》的配套源代码,并做并适配springboot升级版本,完全开源。
SpringBatch的背景和用法,就不再赘述了,默认本文受众都使用过batch框架。
本文仅讨论普通的ChunkStep,分片/异步处理等功能暂不讨论。
1. 表结构
Spring系列的框架代码,大多又臭又长,让人头晕。先列出整体流程,再去看源码。顺带也可以了解存储表结构。
- 每一个jobname,加运行参数的MD5值,被定义为一个job_instance,存储在batch_job_instance表中;
- job_instance每次运行时,会创建一个新的job_execution,存储在batch_job_execution / batch_job_execution_context 表中;
- 扩展:任务重启时,如何续作? 答,判定为任务续作,创建新的job_execution时,会使用旧job_execution的运行态ExecutionContext(通俗讲,火车出故障只换了车头,车厢货物不变。)
- job_execution会根据job排程中的step顺序,逐个执行,逐个转化为step_execution,并存储在batch_step_execution / batch_step_execution_context表中
- 每个step在执行时,会维护step运行状态,当出现异常或者整个step清单执行完成,会更新job_execution的状态
- 在每个step执行前后、job_execution前后,都会通知Listener做回调。
框架使用的表
batch_job_instance
batch_job_execution
batch_job_execution_context
batch_job_execution_params
batch_step_execution
batch_step_execution_context
batch_job_seq
batch_step_execution_seq
batch_job_execution_seq
2. API入口
先看看怎么调用启动Job的API,看起来非常简单,传入job信息和参数即可
@Autowired
@Qualifier("billJob")
private Job job;
@Test
public void billJob() throws Exception {
JobParameters jobParameters = new JobParametersBuilder()
.addLong("currentTimeMillis", System.currentTimeMillis())
.addString("batchNo","2022080402")
.toJobParameters();
JobExecution result = jobLauncher.run(job, jobParameters);
System.out.println(result.toString());
Thread.sleep(6000);
}
<!-- 账单作业 -->
<batch:job id="billJob">
<batch:step id="billStep">
<batch:tasklet transaction-manager="transactionManager">
<batch:chunk reader="csvItemReader" writer="csvItemWriter" processor="creditBillProcessor" commit-interval="3">
</batch:chunk>
</batch:tasklet>
</batch:step>
</batch:job>
org.springframework.batch.core.launch.support.SimpleJobLauncher#run
// 简化部分代码(参数检查、log日志)
@Override
public JobExecution run(final Job job, final JobParameters jobParameters){
final JobExecution jobExecution;
JobExecution lastExecution = jobRepository.getLastJobExecution(job.getName(), jobParameters);
// 上次执行存在,说明本次请求是重启job,先做检查
if (lastExecution != null) {
if (!job.isRestartable()) {
throw new JobRestartException("JobInstance already exists and is not restartable");
}
/* 检查stepExecutions的状态
* validate here if it has stepExecutions that are UNKNOWN, STARTING, STARTED and STOPPING
* retrieve the previous execution and check
*/
for (StepExecution execution : lastExecution.getStepExecutions()) {
BatchStatus status = execution.getStatus();
if (status.isRunning() || status == BatchStatus.STOPPING) {
throw new JobExecutionAlreadyRunningException("A job execution for this job is already running: "
+ lastExecution);
} else if (status == BatchStatus.UNKNOWN) {
throw new JobRestartException(
"Cannot restart step [" + execution.getStepName() + "] from UNKNOWN status. ");
}
}
}
// Check jobParameters
job.getJobParametersValidator().validate(jobParameters);
// 创建JobExecution 同一个job+参数,只能有一个Execution执行器
jobExecution = jobRepository.createJobExecution(job.getName(), jobParameters);
try {
// SyncTaskExecutor 看似是异步,实际是同步执行(可扩展)
taskExecutor.execute(new Runnable() {
@Override
public void run() {
try {
// 关键入口,请看[org.springframework.batch.core.job.AbstractJob#execute]
job.execute(jobExecution);
if (logger.isInfoEnabled())

最低0.47元/天 解锁文章
6107

被折叠的 条评论
为什么被折叠?



