什么是Spring Batch
Spring Batch是Spring 的一个子项目,是一款基于Spring的企业批处理框架,是一个轻量级的、全面的批处理框架。
Spring Batch提供了可重用的功能,不仅提供了统一的读写接口、丰富的任务处理方式、灵活的事务管理以及并发处理,同时还支持日志、监控、任务重启玉跳过等特性。
为什么要使用Spring Batch以及应用的场景
- 为什么要使用
Spring Batch是基于Spring开发的很容易上手
Spring Batch不是一个调度框架,因为它已经有非常好的企业级调度框架包括Quartz等,它只注重任务处理的相关问题,如执行任务、事务、并发等,而不是提供调度功能 - 应用的场景
- 周期提交批处理任务
- 同时批处理任务:并非处理一个任务
- 分阶段的企业消息驱动处理
- 高并发批处理
- 失败后的手动或定时重启
- 按顺序处理任务依赖(使用工作流驱动的批处理插件)
- 部分处理:跳过记录(例如:回滚)
- 全批次事务:因为可能有小数据量的批处理或存在存储过程/脚本
- 批处理的特点
- 数据量大
- 整个过程全部自动化,并预留一定接口进行自定义配置
- 这样的应用通常是周期性运行,比如按日、周、月运行
- 对数据处理的准确性要求高,并且需要容错机制、回滚机制、完善的日志监控等
Spring Batch的处理原则
1. 尽量使用公用模块
2. 尽量简化操作,不要有复杂的业务逻辑,更不要处理一条数据还要调用外部接口进行数据加工
3. 尽可能少的IO操作
4. 同一个批处理文件不要处理两次
5. 尽可能压力测试
Spring Batch的结构
Spring Batch分三层:应用层、核心层和基础架构层
应用层: 开发人员使用Spring Batch编写的所有批处理任务代码
核心层: 包含加载和控制批处理作业所需的核心类,Job,Step等类的实现
基础架构层: 包含统用的读写器和重试模块
Spring batch框架有4个主要组件:JobLauncher、Job、Step和JobRepository。
1)JobLauncher(任务启动器):通过它启动任务,可以理解为程序的入口。
2)Job(任务):一个具体的任务。
3)Step(步骤):一个具体的执行步骤,一个Job中可以有多个Step。
4)JobRepository(任务仓库):存储数据的仓库,在任务执行的时候,需要用它来记录任务状态信息,可以看做是一个数据库的接口。
JobLauncher启动Job,Job可以有多个Step组合,每个Step有开发者自己编写
每一个Step对应一个ItemReader、ItemProcessor和ItemWriter
job : 任务
一个job可以由一个或多个step组成,通过JobBuilderFactory实例创建Bean,使用next指向下一个step;
flow:
1.是多个step的集合;
2.可以被多个Job复用;
3.由flowBuilder来创建的;
split:
实现并发执行;
批处理框架简单抽象成:读取数据、处理数据、写数据
Spring Batch提供了三个接口: ItemReader、ItemProcessor、ItemWriter
JobRepository是记录Job、Step和发起Job的执行信息
Spring Batch的核心概念
-
什么是JobLauncher
JobLauncher是任务启动器,该接口只有一个run方法
public interface JobLauncher { public JobExecution run(Job job, JobParameters jobParameters) throws JobExecutionAlreadyRunningException, JobRestartException, JobInstanceAlreadyCompleteException, JobParametersInvalidException; }
除了传入Job对象外,还需要传入JobParameters对象,通过JobLauncher可以在java程序中调用批处理任务,也可以通过命令行或者其他框架(如定时调度框架Quartz、Web后台框架Spring MVC)中调用批处理任务
-
什么是JOB
Job是一个封装整个批处理过程的概念,体现在代码中是最上层的一个接口
Job的实现类主要有两种:一个是simplejob,另一个是flowjob
一个Job是我们运行的基本单位,它内部由Step组成,Job实质上是Step的一个容器,@Bean public Job footballJob(){ return jobBuilderFactory.get("footballJob") .start(firstStep()) .next(secondStep()) .build(); } 配置的意思是:首先给Job起一个名字footballJob,接着指定这个Job的两个Step(有几个Step指定几个,也可以灵活指定),他们分别由方法firstStep()、secondStep()实现
-
什么是JobInstance
JobInstance指的是逻辑单元的概念,考虑一个批作业,不仅仅执行一次,所以对每个作业的运行必须单独逻辑的JobInstance跟踪,而且每个JobInstance可以多个执行,并且在给定时间内相同参数的同一个JobInstance只能有一个能运行。
JobInstance的定义与要加载的数据完全没有关系
-
什么是JobParameters
同一个Job每天运行一次的话,每天也就都有一个JobInstance,但是如何区分JobInstance呢?那么就是JobParameters
JobParameters对象包含一组用于启动批处理作业的参数,它在运行期间用于识别或者用作参考数据。
JobInstance =Job + JobParameters
-
什么是JobExecution
JobExecution作为一个Job一次执行任务的上下文,因为Job的一个JobInstance有可能失败而多次执行,这样就需要一个上下文来管理同一个JobInstance的多次执行,一次执行有可能失败或成功结束,只有JobExecution执行成功JobInstance才被认为是完成了。
-
什么是Step
Step是一个领域对象,它体现了批处理作业的独立的、连续的阶段。一个Step可以简单也可以复杂,一个简单的步骤可能会将数据从文件加载到数据库中,只需要很少或根本不需要代码,更复杂的步骤可能有作为处理一部分应用的复杂业务规则。与Job一样,Step也有独立的StepExecution存储每一个Step的执行信息。
-
什么是StepExecution
StepExecution表示一次执行Step,每次运行一个Step时都会创建一个新的StepExecution,类似与JobExecution,但是某个步骤可能由于其之前的步骤失败而无法执行,且仅当Step实际启动时才会创建StepExecution。
StepExecution 用来表示每一个step 的执行。每个StepExecution都包含对其相应step和与JobExecution以及事务相关数据的引用,比如提交和回滚计数以及开始和结束时间。此外,每个步骤执行都包含一个ExecutionContext,它包含开发人员在批处理运行期间需要持久化的任何数据,例如重新启动所需的统计信息或状态信息。
-
什么是ExecutionContext
ExecutionContext即每一个StepExecution 的执行环境。它包含一系列的键值对。我们可以用如下代码获取ExecutionContext
ExecutionContext ecStep = stepExecution.getExecutionContext(); ExecutionContext ecJob = jobExecution.getExecutionContext();
-
什么是JobRepository
JobRepository是一个用于将上述job,step等概念进行持久化的一个类。 它同时给Job和Step以及下文会提到的JobLauncher实现提供CRUD操作。 首次启动Job时,将从repository中获取JobExecution,并且在执行批处理的过程中,StepExecution和JobExecution将被存储到repository当中。
@EnableBatchProcessing注解可以为JobRepository提供自动配置。
-
什么是JobLauncher
JobLauncher这个接口的功能非常简单,它是用于启动指定了JobParameters的Job,为什么这里要强调指定了JobParameter,原因jobparameter和job一起才能组成一次job的执行
public interface JobLauncher { public JobExecution run(Job job, JobParameters jobParameters) throws JobExecutionAlreadyRunningException, JobRestartException, JobInstanceAlreadyCompleteException, JobParametersInvalidException; }
-
什么是Item Reader
ItemReader是一个读数据的抽象,它的功能是为每一个Step提供数据输入。 当ItemReader以及读完所有数据时,它会返回null来告诉后续操作数据已经读完。Spring Batch为ItemReader提供了非常多的有用的实现类,比如JdbcPagingItemReader,JdbcCursorItemReader等等。
ItemReader包括各种类型的数据库,文件,数据流,等等JdbcPagingItemReader必须指定一个PagingQueryProvider,负责提供SQL查询语句来按分页返回数据
下面是一个JdbcCursorItemReader的例子代码: private JdbcCursorItemReader<Map<String, Object>> buildItemReader(final DataSource dataSource, String tableName, String tenant) { JdbcCursorItemReader<Map<String, Object>> itemReader = new JdbcCursorItemReader<>(); itemReader.setDataSource(dataSource); itemReader.setSql("sql here"); itemReader.setRowMapper(new RowMapper()); return itemReader; }
-
什么是Item Writer
ItemReader是读数据的一个抽象,那么ItemWriter自然就是一个写数据的抽象,它是为每一个step提供数据写出的功能。写的单位是可以配置的,我们可以一次写一条数据,也可以一次写一个chunk的数据,关于chunk下文会有专门的介绍。ItemWriter对于读入的数据是不能做任何操作的。
-
什么是Item Processor
ItemReader是读数据的一个抽象,那么ItemWriter自然就是一个写数据的抽象,它是为每一个step提供数据写出的功能。写的单位是可以配置的,我们可以一次写一条数据,也可以一次写一个chunk的数据,关于chunk下文会有专门的介绍。ItemWriter对于读入的数据是不能做任何操作的。
-
什么是Chunk
由于我们一次batch的任务可能会有很多的数据读写操作,因此一条一条的处理并向数据库提交的话效率不会很高,因此spring batch提供了chunk这个概念,我们可以设定一个chunk size,spring batch 将一条一条处理数据,但不提交到数据库,只有当处理的数据数量达到chunk size设定的值得时候,才一起去commit.
@Bean public Job footballJob(){ return jobBuilderFactory.get("footballJob") .start(Step1()) .build(); } @Bean public Step Step1() { return stepBuilderFactory.get("Step1") .chunk(10) .reader(itemReader()) .writer(itemWriter()) .build(); } 在上面这个step里面,chunk size被设为了10,当ItemReader读的数据数量达到10的时候, 这一批次的数据就一起被传到itemWriter,同时transaction被提交。
Tasklet 与chunk 的区别:
Tasklet 意味着在step中执行单个任务,job有多个step按一定顺序组成,每个步骤应该执行一个具体任务。
我们的job有三个步骤:
a.从输入csv文件读
b.对每个输入行数据计算年龄
c.写姓名和年龄至输出csv文件Chunk 方法该方法基于数据块(一部分数据)执行。也就是说,其不是一次读、处理和写所有行,而是一次仅读、处理、写固定数量记录。然后重复循环执行直到读不到数据为止。
因此,此流程与上面有些差异:
while 有数据:
do X 行数据
读一行
处理一行
写 X 行数据Tasklet 与chunk 总结:
两者差异显示了各自适用场景。tasklet更适合一个步骤到另一个步骤场景。chunk提供简单解决方案:实现处理分页读,或我们不想在内存中保留大量数据场景。
Spring Batch小案例
Spring Batch完整入门实践
Spring Batch 入门级示例教程
参考文档
Spring Batch参考文档中文版
spring batch 理解、特点、使用场景、结构 简单介绍
Spring Batch 文档和手册(中文)