.png)
技术目标:
批处理开发人员使用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.
.png)
这里做一个实体类映射,加入@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中实际干活的也 ...
		 
		
	
随机推荐
	
									- git pull --rebase的理解
			
在使用git的过程中经常需要使用到git pull命令,在更新远端代码的同时如果与本地代码产生冲突了, 那么冲突的文件中就出现了需要手动合并的部分,而git pull --rebase不同的地方则是当 ...
			 
						- Navicat Premium Mac 12 破解(亲测可用!!!)
			
今天不知怎的,出于强迫症的我就是要强行搞个Navicat Premium Mac 12 破解版本. 历经了种种种种种种磨难与艰辛与火海,终于破解成功了. 因为要经常使用MySQL,使用命令行那是相当的 ...
			 
						- 公式推导【BACF//ICCV2017】
			
HK Galoogahi, A Fagg, S Lucey. Learning Background-Aware Correlation Filters for Visual Tracking[C]. ...
			 
						- HttpUtility.HtmlDecode ,HttpUtility.HtmlEncode  与  Server.HtmlDecode ,Server.HtmlEncode  与 HttpServerUtility.HtmlDecode , HttpServerUtility.HtmlEncode
			
HtmlEncode: 将 Html 源文件中不允许出现的字符进行编码,通常是编码以下字符"<".">"."&" 等.  ...
			 
						- Comment file
			
/// This is the head comment of a file. /*********************************************************** ...
			 
						- CSS3倒影效果
			
比较简单的倒影效果 <pre><div class="box-reflect"><img src="https://www.baidu.co ...
			 
						- 2019.11.21 做OJ题的反思
			
1.利用二分法查找数组元素(适用于有序数组) #include<stdio.h> int BinarySearch(int a[],int n,int key); ]; int main( ...
			 
						- 修改 Oracle 数据库实例字符集
			
Ø  简介 在 Oracle 中创建数据库实例后,就会有对应使用的编码字符集.当我们设置的字符集与操作系统或者其他软件字符集不一致时,就会出现个字符长度存储一个汉字. 2.   SIMPLIFIED  ...
			 
						- linux安装redis步骤
			
1.安装gcc  redis是c语言编写的 -- 安装命令 yum install gcc-c++ -- 检查gcc 是否安装 gcc -v 2.下载redis安装包,在root目录下执行 wget  ...
			 
						- WPF 3D相机基本坐标简介
			
基本概念 WPF中3D空间基本坐标系是右手坐标系. WPF中3D空间的原点是(0,0,0) Position: 这个参数用来表示相机在空间内的坐标.参数是(X,Y,Z).当修改相机的这个参数时,这个坐 ...