文章目录
- 前提须知:分布式调度入门
配置
pom.xml
<dependency> <groupId>com.dangdang</groupId> <artifactId>elastic-job-lite-spring</artifactId> <version>2.1.5</version> </dependency>
application.yml
- 这里的zookeeper设置是自定义的,为了给类通过
${}
使用这里的属性值,解决硬编码问题
zookeeper:
host: localhost:2181
namespace: springboot-elastic-job-demo
server:
port: 8082
准备工作
数据库
实体类
@Data
public class FileCustom {
//唯一标识
private Long id;
//文件名
private String name;
//文件类型
private String type;
//文件内容
private String content;
//是否已备份
private Boolean backedUp = false;
public FileCustom(){}
public FileCustom(Long id, String name, String type, String content){
this.id = id;
this.name = name;
this.type = type;
this.content = content;
}
}
mapper部分
开搞
- 主要讲解以下类
ZookeeperConfig
- 配置zookeeper服务,前提必须启动zookeeper服务
@Configuration
public class ZookeeperConfig {
@Bean
private static CoordinatorRegistryCenter createRegistryCenter(@Value("${zookeeper.host}") String host, @Value("${zookeeper.namespace}") String namespace) {
//配置zk地址,调度任务的组名
ZookeeperConfiguration zookeeperConfiguration = new ZookeeperConfiguration(host, namespace);
zookeeperConfiguration.setSessionTimeoutMilliseconds(100);
CoordinatorRegistryCenter regCenter = new ZookeeperRegistryCenter(zookeeperConfiguration);
regCenter.init();
return regCenter;
}
}
JobConfiguration(重点)
- 任务配置类,分布式调度初始化方法在这里定义,启动启动类则开始初始化(开始定时操作)
- 注意:初始化方法的传入参数要传任务类,createJobConfiguration方法的第一个参数也要传任务类类型
- 此演示注释掉的部分则是Dataflow的方式调度
@Configuration
public class JobConfiguration {
@Autowired
private CoordinatorRegistryCenter coordinatorRegistryCenter;
@Autowired
private DataSource dataSource;
@Bean(initMethod = "init")
public SpringJobScheduler springJobScheduler(FileCustomJob fileCustom) {
//增加任务事件追踪配置
JobEventConfiguration jobEventConfiguration = new JobEventRdbConfiguration(dataSource);
// 1 执行逻辑操作的类 2 zookeeper配置类的方法 3 定时多久执行业务操作的方法
return new SpringJobScheduler(fileCustom, coordinatorRegistryCenter, createJobConfiguration(FileCustomJob.class, "0/10 * * * * ?",
4, "0=text,1=radio,2=vedio,3=image",true),jobEventConfiguration);
}
// @Bean(initMethod = "init")
// public SpringJobScheduler springJobScheduler(MyDataflowJob fileCustom) {
// //增加任务事件追踪配置
// JobEventConfiguration jobEventConfiguration = new JobEventRdbConfiguration(dataSource);
1 执行逻辑操作的类 2 zookeeper配置类的方法 3 定时多久执行业务操作的方法
// return new SpringJobScheduler(fileCustom, coordinatorRegistryCenter, createJobConfiguration(MyDataflowJob.class, "0/10 * * * * ?",
// 1, null,true),jobEventConfiguration);
// }
private static LiteJobConfiguration createJobConfiguration(Class clz, String cron, int shardingCount, String shardingParam, boolean isDataflow) {
// 定义作业核心配置
// 1 任务名称保证唯一 2 cron表达式 3分片参数
JobCoreConfiguration.Builder builder = JobCoreConfiguration.newBuilder(clz.getSimpleName(), cron, shardingCount);
if (!StringUtils.isEmpty(shardingParam)) {
// 设置分片参数
builder.shardingItemParameters(shardingParam);
}
JobCoreConfiguration simpleCoreConfig = builder.build();
JobTypeConfiguration jobTypeConfiguration = null;//
if (isDataflow) {
jobTypeConfiguration = new DataflowJobConfiguration(simpleCoreConfig, clz.getCanonicalName(), true);
} else {
// 定义SIMPLE类型配置
jobTypeConfiguration = new SimpleJobConfiguration(simpleCoreConfig, clz.getCanonicalName());
}
// 定义Lite作业根配置
LiteJobConfiguration simpleJobRootConfig = LiteJobConfiguration.newBuilder(jobTypeConfiguration).overwrite(true).build();
return simpleJobRootConfig;
}
}
分片调度
介绍
-
作业分片是指任务的分布式执行,需要将一个任务拆分为多个独立的任务项,然后由分布式的应用实例分别执行某一个或者几个分布项。例如:Elastic-Job快速入门中文件备份的案例,现有两台服务器,每台服务器分别跑一个应用实例。为了快速执行作业,那么可以讲任务分成4片,每个应用实例都执行两片。作业遍历数据逻辑应为:实例1查找text和image类型文件执行备份,实例2查找radio和vedio类型文件执行备份。如果由于服务器拓容应用实例数量增加为4,则作业遍历数据的逻辑应为: 4个实例分别处理text,image,radio,video类型的文件。
-
可以看到,通过对任务的合理分片化,从而达到任务并行处理的效果.
-
分片项与业务处理解耦
Elastic-Job并不直接提供数据处理的功能,框架只会将分片项分配至各个运行中的作业服务器,开发者需要自行处理分片项与真实数据的对应关系 -
最大限度利用资源
将分片项设置大于服务器的数据,最好是大于服务器倍数的数量,作业将会合理利用分布式资源,动态的分配分片项.
例如: 3台服务器,分成10片,则分片项结果为服务器A=0,1,2;服务器B=3,4,5;服务器C=6,7,8,9.如果 服务器C奔溃,则分片项分配结果为服务器A=0,1,2,3,4;服务器B=5,6,7,8,9.在不丢失分片项的情况下,最大限度利用现有的资源提高吞吐量.
定义分片调度任务类
FileCustomJob类,分片任务类要实现SimpleJob接口
@Component
public class FileCustomJob implements SimpleJob {
@Autowired
private FileCustomMapper fileCustomMapper;
//执行我们定时任务, 比如每隔30分钟就会执行一次execute方法
public void execute(ShardingContext shardingContext) {
// doWork(shardingContext.getShardingParameter());
System.out.println("分片参数: "+shardingContext.getShardingParameter());
}
private void doWork(String shardingParam) {
// 把数据库表中backenUp = 0的数据查询出来
List<FileCustom> fileCustoms =fileCustomMapper.selectByType(shardingParam);
System.out.println("要备份的数据个数为:"+fileCustoms.size());
// 把状态改成1
for (FileCustom fileCustom : fileCustoms){
changStatus(fileCustom);
}
}
private void changStatus(FileCustom fileCustom) {
System.out.println("要备份数据的id:"+fileCustom.getId()+" 要备份数据的类型:"+fileCustom.getType());
try {
Thread.sleep(1000);
}catch (Exception e){
e.printStackTrace();
}
fileCustomMapper.changStatus(fileCustom.getId(),1);
}
}
Dataflow调度
介绍
-
Dataflow类型的定时任务需要实现Dataflowjob接口,该接口提供2个方法供覆盖,分别用于抓取(fetchData)和处理(processData)数据,我们继续对例子进行改造。
-
Dataflow类型用于处理数据流,他和SimpleJob不同,它以数据流的方式执行,调用fetchData抓取数据,直到抓取不到数据才停止作业。
定义Dataflow调度任务类
- 实现DataflowJob<>接口,<> 里面存放要被调度使用的实体类
@Component
public class MyDataflowJob implements DataflowJob<FileCustom> {
@Autowired
private FileCustomMapper fileCustomMapper;
/**
* 抓取数据方法
* @param shardingContext
* 一次性抓取2条数据 把2条数据的backedUp 由0->1
* @return
*/
@Override
public List<FileCustom> fetchData(ShardingContext shardingContext) {
return fileCustomMapper.fetchData(2);
}
@Override
public void processData(ShardingContext shardingContext, List<FileCustom> list) {
System.out.println("要备份的数据个数为:"+list.size());
// 把状态改成1
for (FileCustom fileCustom : list){
changStatus(fileCustom);
}
}
private void changStatus(FileCustom fileCustom) {
System.out.println("要备份数据的id:"+fileCustom.getId()+" 要备份数据的类型:"+fileCustom.getType());
try {
Thread.sleep(1000);
}catch (Exception e){
e.printStackTrace();
}
fileCustomMapper.changStatus(fileCustom.getId(),1);
}
}