Spring Batch远程分区的本地Jar包模式
1 前言
欢迎访问南瓜慢说 www.pkslow.com获取更多精彩文章!
Spring相关文章:
Springboot-Cloud
Spring Batch
远程分区对于大量数据的处理非常擅长,它的实现有多种方式,如本地Jar包模式
、MQ模式
、Kubernetes模式
。这三种模式的如下:
(1)本地Jar包模式
:分区处理的worker
为一个Java进程
,从jar
包启动,通过jvm
参数和数据库传递参数;官方提供示例代码。
(2)MQ模式
:worker
是一个常驻进程,Manager
和Worker
通过消息队列来传递参数;网上有不少相关示例代码。
(3)Kubernetes模式
:worker
为K8s
中的Pod
,Manager
直接启动Pod
来处理;网上并没有找到任何示例代码。
本文将通过代码来讲解第一种模式(本地Jar包模式
),其它后续再介绍。
建议先看下面文章了解一下:
Spring Batch入门:通过例子讲解Spring Batch入门,优秀的批处理框架
Spring Batch并行处理介绍:大量数据也不在话下,Spring Batch并行处理四种模式初探
2 代码讲解
本文代码中,Manager
和Worker
是放在一起的,在同一个项目里,也只会打一个jar
包而已;我们通过profile
来区别是manager
还是worker
,也就是通过Spring Profile
实现一份代码,两份逻辑。实际上也可以拆成两份代码,但放一起更方便测试,而且代码量不大,就没有必要了。
2.1 项目准备
2.1.1 数据库
首先我们需要准备一个数据库,因为Manager
和Worker
都需要同步状态到DB
上,不能直接使用嵌入式的内存数据库了,需要一个外部可共同访问的数据库。这里我使用的是H2 Database
,安装可参考:把H2数据库从jar包部署到Kubernetes,并解决Ingress不支持TCP的问题。
2.1.2 引入依赖
maven
引入依赖如下所示:
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-batch</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-task</artifactId>
</dependency>
<dependency>
<groupId>com.h2database</groupId>
<artifactId>h2</artifactId>
<scope>runtime</scope>
</dependency>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-deployer-local</artifactId>
<version>2.4.1</version>
</dependency>
<dependency>
<groupId>org.springframework.batch</groupId>
<artifactId>spring-batch-integration</artifactId>
</dependency>
spring-cloud-deployer-local
用于部署和启动worker
,非常关键;其它就是Spring Batch
和Task
相关的依赖;以及数据库连接。
2.1.3 主类入口
Springboot
的主类入口如下:
@EnableTask
@SpringBootApplication
@EnableBatchProcessing
public class PkslowRemotePartitionJar {
public static void main(String[] args) {
SpringApplication.run(PkslowRemotePartitionJar.class, args);
}
}
在Springboot
的基础上,添加了Spring Batch
和Spring Cloud Task
的支持。
2.2 关键代码编写
前面的数据库搭建和其它代码没有太多可讲的,接下来就开始关键代码的编写。
2.2.1 分区管理Partitioner
Partitioner
是远程分区中的核心bean
,它定义了分成多少个区、怎么分区,要把什么变量传递给worker
。它会返回一组<分区名,执行上下文>的键值对,即返回Map<String, ExecutionContext>
。把要传递给worker
的变量放在ExecutionContext
中去,支持多种类型的变量,如String
、int
、long
等。实际上,我们不建议通过ExecutionContext
来传递太多数据;可以传递一些标识或主键,然后worker
自己去拿数据即可。
具体代码如下:
private static final int GRID_SIZE = 4;
@Bean
public Partitioner partitioner() {
return new Partitioner() {
@Override
public Map<String, ExecutionContext> partition(int gridSize) {
Map<String, ExecutionContext> partitions = new HashMap<>(gridSize);
for (int i = 0; i < GRID_SIZE; i++) {
ExecutionContext executionContext = new ExecutionContext();
executionContext.put("partitionNumber", i);
partitions.put("partition" + i, executionContext);
}
return partitions;
}
};
}
上面分成4个区,程序会启动4个worker
来处理;给worker
传递的参数是partitionNumber
。
2.2.2 分区处理器PartitionHandler
PartitionHandler
也是核心的bean
,它决定了怎么去启动worker
,给它们传递什么jvm
参数(跟之前的ExecutionContext
传递不一样)。
@Bean
public PartitionHandler partitionHandler(TaskLauncher taskLauncher, JobExplorer jobExplorer, TaskRepository taskRepository) throws Exception {
Resource resource = this.resourceLoader.getResource(workerResource);
DeployerPartitionHandler partitionHandler =
new DeployerPartitionHandler(taskLauncher, jobExplorer, resource, "workerStep", taskRepository);
List<String> commandLineArgs = new ArrayList<>(3);
commandLineArgs.add("--spring.profiles.active=worker");
commandLineArgs.add("--spring.cloud.task.initialize-enabled=false");
commandLineArgs.add("--spring.batch.initializer.enabled=false");
partitionHandler
.setCommandLineArgsProvider(new PassThroughCommandLineArgsProvider(commandLineArgs));
partitionHandler
.setEnvironmentVariablesProvider(new SimpleEnvironmentVariablesProvider(this.environment));
partitionHandler.setMaxWorkers(2);
partitionHandler.setApplicationName("PkslowWorkerJob");
return partitionHandler;
}
上面代码中:
resource
是worker
的jar
包地址,表示将启动该程序;
workerStep
是worker
将要执行的step
;
commandLineArgs
定义了启动worker
的jvm
参数,如--spring.profiles.active=worker
;
environment
是manager
的系统环境变量,可以传递给worker
,当然也可以选择不传递;
MaxWorkers
是最多能同时启动多少个worker
,类似于线程池大小;设置为2,表示最多同时有2个worker
来处理4个分区。
2.2.3 Manager和Worker的Batch定义
完成了分区相关的代码,剩下的就只是如何定义Manager
和Worker
的业务代码了。
Manager
作为管理者,不用太多业务逻辑,代码如下:
@Bean
@Profile("!worker")
public Job partitionedJob(PartitionHandler partitionHandler) throws Exception {
Random random = new Random();
return this.jobBuilderFactory.get("partitionedJob" + random.nextInt())
.start(step1(partitionHandler))
.build();
}
@Bean
public Step step1(PartitionHandler partitionHandler) throws Exception {
return this.stepBuilderFactory.get("step1")
.partitioner(workerStep().getName(), partitioner())
.step(workerStep())
.partitionHandler(partitionHandler)
.build();
}
Worker
主要作用是处理数据,是我们的业务代码,这里就演示一下如何获取Manager
传递过来的partitionNumber
:
@Bean
public Step workerStep() {
return this.stepBuilderFactory.get("workerStep")
.tasklet(workerTasklet(null, null))
.build();
}
@Bean
@StepScope
public Tasklet workerTasklet(final @Value("#{stepExecutionContext['partitionNumber']}") Integer partitionNumber) {
return new Tasklet() {
@Override
public RepeatStatus execute(StepContribution contribution, ChunkContext chunkContext) throws Exception {
Thread.sleep(6000); //增加延时,查看效果,通过jps:在jar情况下会新起java进程
System.out.println("This tasklet ran partition: " + partitionNumber);
return RepeatStatus.FINISHED;
}
};
}
通过表达式@Value("#{stepExecutionContext['partitionNumber']}")
获取Manager
传递过来的变量;注意要加注解@StepScope
。
3 程序运行
因为我们分为Manager
和Worker
,但都是同一份代码,所以我们先打包一个jar
出来,不然manager
无法启动。配置数据库和Worker
的jar
包地址如下:
spring.datasource.url=jdbc:h2:tcp://localhost:9092/test
spring.datasource.username=pkslow
spring.datasource.password=pkslow
spring.datasource.driver-class-name=org.h2.Driver
pkslow.worker.resource=file://pkslow/target/remote-partitioning-jar-1.0-SNAPSHOT.jar
执行程序如下:
可以看到启动了4次Java
程序,还给出日志路径。
通过jps
命令查看,能看到一个Manager
进程,还有两个worker
进程:
4 复杂变量传递
前面讲了Manager
可以通过ExecutionContext
传递变量,如简单的String
、long
等。但其实它也是可以传递复杂的Java
对象的,但对应的类需要可序列化,如:
import java.io.Serializable;
public class Person implements Serializable {
private Integer age;
private String name;
private String webSite;
//getter and setter
}
Manager
传递:
executionContext.put("person", new Person(0, "pkslow", "www.pkslow.com"));
Worker
接收:
@Value("#{stepExecutionContext['person']}") Person person
5 总结
本文介绍了Spring Batch
远程分区的本地Jar包模式
,只能在一台机器上运行,所以也是无法真正发挥出远程分区的作用。但它对我们后续理解更复杂的模式是有很大帮助的;同时,我们也可以使用本地模式进行开发测试,毕竟它只需要一个数据库就行了,依赖很少。
欢迎关注微信公众号<南瓜慢说>,将持续为你更新...
多读书,多分享;多写作,多整理。
Spring Batch远程分区的本地Jar包模式的更多相关文章
- Spring Batch 远程分区和远程分块的区别
Partitioning is a master/slave step configuration that allows for partitions of data to be processed ...
- 如何将本地jar包放入本地maven仓库和远程私服仓库
1.将本地jar包放入本地仓库.只需执行如下命令即可: mvn install:install-file -Dfile=D:/demo/fiber.jar -DgroupId=com.sure -Da ...
- (转)如何在maven的pom.xml中添加本地jar包
1 maven本地仓库认识 maven本地仓库中的jar目录一般分为三层:图中的1 2 3分别如下所示: 1 groupId 2 artifactId 3 version 4 jar包的依赖 如果要将 ...
- (转)如何在maven的pom.xml中添加本地jar包
转载自: https://www.cnblogs.com/lixuwu/p/5855031.html 1 maven本地仓库认识 maven本地仓库中的jar目录一般分为三层:图中的1 2 3分别如下 ...
- maven 把本地jar包打进本地仓库
maven 把本地jar包打进本地仓库 1.本地有自己写的项目jar包,但是需要用maven依赖对其进行引用: 2.某个jar包在远程仓库没有,导致pom.xml报错,此时可以从网上单独下载此jar包 ...
- maven引用本地jar包
教程:http://www.cnblogs.com/dcba1112/archive/2011/05/01/2033805.html 安装 到下载maven: http://maven.apache. ...
- 本地jar包上传docker容器
先安装docker的注册服务器: [root@VM_0_7_centos ~]# docker run -d -p : --restart=always --name registry2 regist ...
- Maven安装本地jar包到本地仓库
Maven 安装 JAR 包到本地仓库的命令是: mvn install:install-file -Dfile=jar包的位置 -DgroupId=上面的groupId -DartifactId=上 ...
- Spring 框架介绍 [Spring 优点][Spring 应用领域][体系结构][目录结构][基础 jar 包]
您的"关注"和"点赞",是信任,是认可,是支持,是动力...... 如意见相佐,可留言. 本人必将竭尽全力试图做到准确和全面,终其一生进行修改补充更新. 目录 ...
随机推荐
- Django context must be a dict ranther than Context
1.1 错误描述 TypeError at /time/ context must be a dict rather than Context. Request Method: GET Request ...
- 《MySQL必知必会》过滤数据,数据过滤(where ,in ,null ,not)
<MySQL必知必会>过滤数据,数据过滤 1.过滤数据 1.1 使用 where 子句 在SEL ECT语句中,数据根据WHERE子句中指定的搜索条件进行过滤. WHERE子句在表名(FR ...
- Salesforce学习笔记之Actions and Recommendations
设置Actions and Recommendations(Salesforce提供的标准元素),Salesforce上的文档说有两种方法,即Deployment和Process Builder(通过 ...
- 汇编in和out实例解析
直接看例子: IN AL,21H 从21H端口读取一字节数据到AL IN AX,21H 从端口地址21H读取1字节数据到AL,从端口地址22H读取1字节到AH MOV DX,379HIN AL,DX ...
- SpringSecurity权限管理系统实战—七、处理一些问题
目录 SpringSecurity权限管理系统实战-一.项目简介和开发环境准备 SpringSecurity权限管理系统实战-二.日志.接口文档等实现 SpringSecurity权限管理系统实战-三 ...
- 多线程系列(二)之Thread类
在上一遍文章中讲到多线程基础,在此篇文章中我们来学习C#里面Thread类.Thread类是在.net framework1.0版本中推出的API.如果对线程的概念还不太清楚 的小伙伴请阅读我的上一遍 ...
- Linux环境下安装Redis数据库
1.下载Redis安装包 访问https://redis.io/download,目前最新版本是5.0.5,点击下载 2.安装Redis 2.1通过远程工具把压缩包导入Linux工作盘,我的在home ...
- 算法-搜索(3)AVL树
AVL树高度平衡的二叉搜索树,任一点的平衡印章只能是+1.-1.0,从而尽量降低树的高度. 如果它有n个结点,高度可保持在O(log2n),平均搜索长度也可保持在O(log2n). (1)AVL树的插 ...
- Java并发---concurrent包
一.包的结构层次 其中包含了两个子包atomic和locks,另外字concurrent下的阻塞队列以及executor,这些就是concurrent包中的精华.而这些类的实现主要是依赖于volati ...
- 微信小程序——导航栏组件
组件内属性详解 属性 类型 默认值 必填 说明 nav-postion String relative 否 导航栏(包含导航栏以及状态栏)的position,可取值relative.fixed.a ...