技术目标:
批处理开发人员使用Spring编程模型: 专注于业务逻辑,并让框架处理基础结构
在基础结构,批处理执行环境和批处理应用程序之间明确分离关注点.
提供通用的核心执行服务作为所有项目都可以实现的接口
提供可以直接使用的核心执行接口的简单和默认实现.
通过在所有层中利用spring框架,轻松配置,定制和扩展服务.
所有现有的核心服务应易于替换或扩展,而不会影响基础架构层.
提供一个简单的部署模型,其架构JAR与使用Maven构建的应用程序完全分开.
sf.gg
Spring Batch 结构
首先,Spring Batch运行的基本单位是一个Job,一个Job就做一件批处理的事情.
一个Job包含很多Step,step就是每个job要执行的单个步骤.
如下图,Step(步骤)里面,会有Tasklet(小任务,任务单元),Tasklet是一个任务单元,它是属于可以重复利用的东西.
然后是Chunk(数据块),chunk就是数据块,你需要定义多大的数据量是一个chunk.
Chunk里面就是不断循环的一个流程,读数据,处理数据,然后写数据.Spring Batch会不断的循环这个流程,直到批处理数据完成.
构建Spring Batch :
首先,我们需要一个全局的Configuration来配置所有的Job和一些全局配置.
@Configuration@EnableAutoConfiguration@EnableBatchProcessing(modular = true)
public class SpringBatchConfiguration {
@Bean
public ApplicationContextFactory firstJobContext() {
return new GenericApplicationContextFactory(FirstJobConfiguration.class);
}
@Bean
public ApplicationContextFactory secondJobContext() {
return new GenericApplicationContextFactory(SecondJobConfiguration.class);
}
}
两个bean的实例化,每个bean是一个任务配置
注解@EnableBatchProcessing(modular = true)
这个注解是打开Batch,如果要实现多Job的情况,需要把EnableBatchProcessing注解
modular设置为true,让每个Job使用自己的ApplicationContext.
比如上面代码就创建了两个Job.
这里做一个实体类映射,加入@Entity, @Table注解
首先构建Job
首先我们需要一个关于这个Job的Configuration,它将在SpringBatchConfiguration里被加载.
@Configuration
@EnableAutoConfiguration
@EnableBatchProcessing(modular = true)
public class SpringBatchConfiguration {
@Bean
public ApplicationContextFactory messageMigrationJobContext() {
return new GenericApplicationContextFactory(MessageMigrationJobConfiguration,class);
}
}
下面的关于构建Job的代码都将写在这个MessageMigrationJobConfiguration里面
public class MessageMigrationJobConfiguration {
//调数据 执行 写数据
}
我们先定义一个Job的Bean
@Autowired
private JobBuilderFactory jobBuilderFactory;
@Bean
public Job messageMigrationJob(@Qualifier("messageMigrationStep") Step messageMigrationStep) {
return jobBuilderFactory.get("messageMigrationJob")
.start(messageMigrationStep)
.build();
}
jobBuilderFactory是注入进来的,get里面的就是job的名字.
这个job只有一个step.
Step
接下来就是创建Step
@Autowired
private StepBuilderFactory stepBuilderFactory;
@Bean
public Step messageMigrationStep(@Qualifier("jsonMessageReader") FlatFileItemReader<Message> jsonMessageReader,
@Qualifier("messageItemWriter") JpaItemWriter<Message> messageItemWriter,
@Qualifier("errorWriter") Writer errorWriter) {
return stepBuilderFactory.get("messageMigrationStep")
.<Message, Message>chunk(CHUNK_SIZE)
.reader(jsonMessageReader).faultTolerant().skip(JsonParseException.class).skipLimit(SKIP_LIMIT)
.listener(new MessageItemReadListener(errorWriter))
.writer(messageItemWriter).faultTolerant().skip(Exception.class).skipLimit(SKIP_LIMIT)
.listener(new MessageWriteListener())
.build();
}
stepBuilderFactory是注入进来的,然后get里面是Step的名字.
我们的Step中可以构建很多东西,比如reader,processer,writer,listener等等.
下面我们就逐个来看看step里面的这些东西是如何使用的.
Chunk
Spring batch在配置Step时采用的是基于Chunk的机制,即每次读取一条数据,再处理一条数据,累积到
一定数量后再一次性交给writer进行写入操作. 这样可以最大化的优化写入效率,整个事务也是基于Chunk
来进行.
比如我们定义chunk size是50,那就意味着,spring batch处理了50条数据后,再统一向数据库写入.
这里有个很重要的点,chunk前面需要定义数据输入类型和输出类型,由于我们输入是Message,输出
也是Message,所以两个都直接写Message了.
如果不定义这个类型,会报错.
.<Message,Message> chunk(CHUNK_SIZE)
Reader (读取器)
Reader顾名思义就是从数据源读取数据.
Spring Batch给我们提供了很多好用实用的reader, 基本能满足我们所有需求.
比如FlatFileItemReader, JdbcCursorItemReader, JpaPagingItemReader 等.
也可以自己实现Reader.
本例子里面,数据源是文本文件,所以我们就使用FlatFileItemReader. FlatFileItemReader是从文件里面
一行一行的读取数据.
首先需要设置文件路径,也就是设置resource.
因为我们需要把一行文本映射为Message类,所以我们需要自己设置并实现LineMapper.
@Bean
public FlatFileItemReader<Message> jsonMessageReader() {
FlatFileItemReader<Message> reader = new FlatFileItemReader<>();
reader.setResource(new FileSystemResource(new File(MESSAGE_FILE)));
reader.setLineMapper(new MessageLineMapper());
return reader;
}
Line Mapper (行读取工具)
LineMapper的输入就是获取一行文本,和行号,然后转换成Message.
在本例子里面,一行文本就是一个json对象,所以我们使用JsonParser来转换成Message.
public class MessageLineMapper implements LineMapper<Message> {
private MappingJsonFactory factory = new MappingJsonFactory();
@Override
public Message mapLine(String line,int lineNumber) throws Exception {
JsonParser parser = factory.createParser(line);
Map<String,Object> map = (Map)parser.readValueAs(Map.class);
Message message = new Message();
//转换逻辑
return message;
}
}
Processor (处理程序)
由于本例子里面,数据是一行文本,通过reader变成Message的类,然后writer直接把Message写入MySQL.
所以我们的例子里面就不需要Processor, 关于如何写Processor其实和reader/writer是一样的道理.
从它的接口可以看出,需要定义输入和输出的类型,把输入I通过某些逻辑处理之后,返回输出O.
public interface ItemProcessor<I,O> {
O process(I item) throws Exception;
}
Writer (写入者)
Writer顾名思义就是把数据写入到目标数据源里面.
Spring Batch同样给我们提供很多好用实用的writer. 比如JpaItemWriter, FlatFileItemWriter, HibernateItemWriter, JdbcBatchItemWriter.
同样也可以自定义.
本例子里面,使用的是JpaItemWriter, 可以直接把Message对象写入到数据库里面, 但是需要设置一个EntityManagerFactory,可以注入进来.
@Autowired
private EntityManagerFactory entityManager;
@Bean
public JpaItemWriter<Message> messageItemWriter() {
JpaItemWriter<Message> writer = new JpaItemWriter<>();
writer.setEntityManagerFactory(entityManager);
return writer;
}
另外,你需要配置数据库的连接等东西, 由于我使用的spring,所以直接在Application.properties里面配置如下:
spring.datasource.url=jdbc:mysql://database
spring.datasource.username=username
spring.datasource.password=password
spring.datasource.driver-class-name=com.mysql.cj.jdbc.Driver
...
...
spring.datasource相关的设置都是在配置数据库的连接.
spring.batch.initialize-schema=always表示让spring batch在数据库里面创建默认的数据表.
Listener
Spring Batch同样实现了非常完善全面的listener, listener很好理解,就是用来监听每个步骤的结果.
比如可以有监听step的, job, reader,writer的.
本例博主关心的是read的时候有没有出错,和write的时候有没有出错,所以,我只实现了ReadListener和WriteListener
在read出错的时候,把错误结果写入一个单独的error列表文件中.
public class MessageItemReadListener implements ItemReadListener<Message> {
private Writer errorWriter;
public MessageItemReadListener(Writer errorWriter) {
this.errorWriter = errorWriter;
}
@Override
public void beforeRead(){
}
@Override
public void afterRead(Message item){
}
@Override
public void onReadError(Exception ex){
errorWriter.write(format("%s%n",ex.getMessage()));
}
}
在write出错的时候,也做同样的事情, 把出错的原因写入单独的日志中.
public class MessageWriteListener implements ItemWriteListener<Message> {
@Autowired
private Writer errorWriter;
@Override
public void beforeWrite(List<? extends Message> items) {
}
@Override
public void afterWrite(List<? extends Message> items) {
}
@Override
public void onWriteError(Exception exception, List<? extends Message> items) {
errorWriter.write(format("%s%n", exception.getMessage()));
for (Message message : items) {
errorWriter.write(format("Failed writing message id: %s", message.getObjectId()));
}
}
}
前面有说chunk机制,所以write的listener传入参数是一个List,因为它是累积到一定的数量才一起写入.
Skip
Spring Batch提供了skip的机制,也就是说,如果出错了,可以跳过. 如果你不设置skip,那么一条数据出错了,整个job都会挂掉.
设置skip的时候一定要设置什么Exception才需要跳过,并且跳过多少条数据. 如果失败的数据超过你设置的skip limit,那么job就会失败.
你可以分别给reader和writer等设置skip机制.
writer(messageItemWriter).faultTolerant().skip(Exception.class).skipLimit(SKIP_LIMIT)
Retry
这个和Skip是一样的原理,就是失败之后可以重试,你同样需要设置重试的次数.
同样可以分别给reader,writer等设置retry机制.
如果同时设置了retry和skip,会先重试所有次数,然后再开始skip, 比如retry是10次,skip是20,会先重试10次之后,再开始算第一次skip.
运行Job
这里博主说的Job运行方法是通过main方法,获取到args的参数从jobRegistry中获取该job, 通过jobLauncher.run运行该job
public static void main(String[] args) {
String jobName = args[0];
try {
ConfigurableApplicationContext context = SpringApplication.run(ZuociBatchApplication.class, args);
JobRegistry jobRegistry = context.getBean(JobRegistry.class);
Job job = jobRegistry.getJob(jobName);
JobLauncher jobLauncher = context.getBean(JobLauncher.class);
JobExecution jobExecution = jobLauncher.run(job, createJobParams());
if (!jobExecution.getExitStatus().equals(ExitStatus.COMPLETED)) {
throw new RuntimeException(format("%s Job execution failed.", jobName));
}
} catch (Exception e) {
throw new RuntimeException(format("%s Job execution failed.", jobName));
}
}
private static JobParameters createJobParams() {
return new JobParametersBuilder().addDate("date", new Date()).toJobParameters();
}
public static void main(String[] args) {
String jobName = args[0];
try {
ConfigurableApplicationContext context = SpringApplication.run(ZuociBatchApplication.class, args);
JobRegistry jobRegistry = context.getBean(JobRegistry.class);
Job job = jobRegistry.getJob(jobName);
JobLauncher jobLauncher = context.getBean(JobLauncher.class);
JobExecution jobExecution = jobLauncher.run(job, createJobParams());
throw new RuntimeException(format("%s Job execution failed.", jobName));
}
} catch (Exception e) {
throw new RuntimeException(format("%s Job execution failed.", jobName));
}
}
private static JobParameters createJobParams() {
return new JobParametersBuilder().addDate("date", new Date()).toJobParameters();
}
最后,把jar包编译出来,在命令行执行下面的命令,就可以运行你的Spring Batch了
java -jar YOUR_BATCH_NAME.jar YOUR_JOB_NAME
从这可以看出批处理的执行可以分为一个个微服务 (springboot) 去单独运行.
info调试
调试主要依靠控制台输出的log,可以在application.properties里面设置log输出的级别,比如你希望输出INFO信息还是DEBUG信息。
基本上,通过查看log都能定位到问题。
logging.path=build/logs
logging.file=${logging.path}/batch.log
logging.level.com.easystudio=INFO
logging.level.root=INFO
log4j.logger.org.springframework.jdbc=INFO
log4j.logger.org.springframework.batch=INFO
logging.level.org.hibernate.SQL=INFO
Spring Batch数据表
如果你的batch最终会写入数据库,那么Spring Batch会默认在你的数据库里面创建一些batch相关的表.
来记录所有job/step运行的状态和结果.
如果你的batch最终会写入数据库,那么Spring Batch会默认在你的数据库里面创建一些batch相关的表,
来记录所有job/step运行的状态和结果.
大部分表你都不需要关心,你只需要关心几张表.
batch_job_instance : 这张表能看到每次运行的job名字:
batch_job_execution : 这张表能看到每次运行job的开始时间,结束时间,状态,以及失败后的错误消息是什么.
batch_step_execution : 这张表你能看到更多关于step的详细信息,比如step的开始时间,结束时间,提交次数,读写次数,状态,以及失败后的错误信息等.
总结
Spring Batch为我们提供了非常实用的功能,对批处理场景进行了完善的抽象,它不仅能实现小数据的迁移,也能应对大企业的大数据实践应用.
它让我们开发批处理应用可以事半功倍.
tips: 搭建Spring Batch的过程中,会遇到各种各样的问题, 只要善用Google,都能找到答案.
- spring batch批量处理框架
spring batch精选,一文吃透spring batch批量处理框架 前言碎语 批处理是企业级业务系统不可或缺的一部分,spring batch是一个轻量级的综合性批处理框架,可用于开发企业信息 ...
- 史上最轻松入门之Spring Batch - 轻量级批处理框架实践
从 MariaDB 一张表内读 10 万条记录,经处理后写到 MongoDB . Batch 任务模型 具体实现 1.新建 Spring Boot 应用,依赖如下: <!-- Web 应用 -- ...
- 跑批 - Spring Batch 批处理使用记录
根据spring官网文档提供的spring batch的demo进行小的测验 启动类与原springboot启动类无异 package com.example.batchprocessing; imp ...
- 【转】大数据批处理框架 Spring Batch全面解析
如今微服务架构讨论的如火如荼.但在企业架构里除了大量的OLTP交易外,还存在海量的批处理交易.在诸如银行的金融机构中,每天有3-4万笔的批处理作业需要处理.针对OLTP,业界有大量的开源框架.优秀的架 ...
- 通过例子讲解Spring Batch入门,优秀的批处理框架
1 前言 欢迎访问南瓜慢说 www.pkslow.com获取更多精彩文章! Spring相关文章:Springboot-Cloud相关 Spring Batch是一个轻量级的.完善的批处理框架,作为S ...
- Spring Batch 批处理框架
<Spring Batch 批处理框架>基本信息作者: 刘相 出版社:电子工业出版社ISBN:9787121252419上架时间:2015-1-24出版日期:2015 年2月开本:16开页 ...
- 图书简介:Spring Batch批处理框架
大数据时代批处理利器,国内首度原创解析Spring Batch框架. 内容简介: <Spring Batch 批处理框架>全面.系统地介绍了批处理框架Spring Batch,通过详尽的实 ...
- 分布式定时任务框架比较,spring batch, tbschedule jobserver
分布式定时任务框架比较,spring batch, tbschedule jobserver | 移动开发参考书 分布式定时任务框架比较,spring batch, tbschedule jobser ...
- Spring Batch框架流程的简单介绍
Spring Batch流程介绍: 上图描绘了Spring Batch的执行过程.说明如下: 每个Batch都会包含一个Job.Job就像一个容器,这个容器里装了若干Step,Batch中实际干活的也 ...
随机推荐
- HTML连载40-盒子宽度和高度的练习、box-sizing属性
一.判断方法 1.判断是否元素宽高为200的盒子 只需要看:边框+内边距+内容宽度/内容高度的值是否等于200 2.判断是否内容宽高为100的盒子 只需要看:width和heght的值是否等于100 ...
- macbook pro更换键盘的排线
以下是拆机图解 很简单的 有点动手能力的都可以更换 另外附加淘宝上的链接 https://item.taobao.com/item.htm?spm=a1z09.2.0.0.68a12e8dYAiQIx ...
- 快速了解Electron:新一代基于Web的跨平台桌面技术
本文引用了作者“ ConardLi”的<用JS开发跨平台桌面应用,从原理到实践>一文部分内容,原文链接:segmentfault.com/a/1190000019426512,感谢原作者的 ...
- redis之主从同步
很多企业都没有使用到 Redis 的集群,但是至少都做了主从.有了主从,当 master 挂掉的时候,运维让从库过来接管,服务就可以继续,否则 master 需要经过数据恢复和重启的过程,这就可能会拖 ...
- 第四节:Geo类型介绍以及Redis批量操作、事务、分布式锁
一. Geo类型 1. 类型说明 Geo 是 Redis 3.2 版本后新增的数据类型,用来保存兴趣点(POI,point of interest)的坐标信息.可以实现计算两 POI 之间的距离.获取 ...
- Navicat Premium 12.0.22 安装与破解
一.安装 Navicat Premium 12.0.22的下载链接:https://pan.baidu.com/s/1swRY_fwIZfufdxDZj3hDyw 密码:09k8 安装步骤就是一路向下 ...
- GAC 解释&路径
GAC 全称是 Global Assembly Cache 作用是可以存放一些有很多程序都要用到的公共 Assembly ,例如 System.Data .System.Windows.Form 等等 ...
- Javaweb常用解决问题连接
1.javaweb的idea如何创建及配置web项目 https://www.jianshu.com/p/8d49d36a3c7e 2.servlet的建立以及部署 https://blog.csdn ...
- Navicat for Mysql安装及破解教程
一.Navicat for Mysql安装 下载链接:https://navicatformysql.en.softonic.com/ 点击download下载. 下载完成后双击安装 二.破解 破解工 ...
- php 除10取整,取十位数前面一个数字,百位前两个数字
需求:php 除10取整,取十位数前面一个数字,百位前两个数字,并把大于2的加红显示 例:0-9,10-19,20-29,30-39,110-119对应为:0 1 2 3 11 实现主要方法:$num ...