介绍一下:ElasticJob
ElaticJob,是一个可以定时任务的框架,提供了作业分片策略,定时任务追综,作业运行状态监控,作业监听器,自诊断修复等功能的一个分布式的定时任务框架.注意:该篇节是以SPringElaticJob 为基础的一个介绍
前言:
我们如何实现分布式的定时任务
如果让我们来写一个分布式的定时任务,我们应该如何使用,其实也很简单,我们知道Java的内存或者说直接内存,是在当前任务或者进程开辟的一块内存空间,那么其他进程是无法访问这块内存空间的.我们要实现分布式的定时任务,就需要首先解决这个问题.
Zookeeper: 这是一个不错的选择
解决一: 我们来了解一下什么是Zookeeper,这是一个节点的存储框架,而且可以进行节点监听,感知节点的上下线,在连接上Zk之后,我们可以对zk进行节点输出,节点监控,子节点数据监控,节点数据监控,子节点数据监控,等操作,那么通过简短的介绍,我们可以了解到zk是可以满足节点的上下线监控和选举的操作的;
那动态监听节点的问题可以通过ZK来解决,接下来我们要分析一下,如果执行定时任务,那么就需要有cron表达式,和ElasticJob的分片操作,那么他是如何来解决的呢
解决二: 如何实现分片,我们在定义一个定时任务配置的时候,我们可以在参数上添加描述Sharding 分片,也就是说会分几片来进行执行,他有三种规则,公平,哈希ip,自定义分片策略,实现JobShardingStrategy接口并实现sharding方法,接口方法参数为作业服务器IP列表和分片策略选项,分片策略选项包括作业名称,分片总数以及分片序列号和个性化参数对照表,可以根据需求定制化自己的分片策略。
如何写配置:
server:
port: 8088
elasticjob: #这个是我们自己在初始化的时候区的非框架配置
zookeeper-url: localhost:2181 #zookeeper-url 的链接端口
group-name: elastic-job-group #这个是zooker 在可视化界面创建项目的名字
spring:
datasource: #数据源配置
url: jdbc:mysql://localhost:3306/elastic-job-demo?serverTimezone=GMT%2B8
driverClassName: com.mysql.cj.jdbc.Driver
type: com.alibaba.druid.pool.DruidDataSource
username: root
password: admin
一: 简单的一个定时任务如何使用
第一步:Maven依赖(Spring的)
<dependency>
<groupId>com.dangdang</groupId>
<artifactId>elastic-job-lite-spring</artifactId>
<version>2.1.5</version>
</dependency>
第二步:定义一个Zk的连接器
这一步的意义,就是连接zk,一个注册中心,可以让Elasticjob感知到的一个注册中心,并且可以自由使用,也是说这是实现分片的核心技术的一个展现
//简单的
@Configuration
public class CoordinatorRegistryConfig {
@Bean(initMethod = "init")
private static CoordinatorRegistryCenter createRegisterCenter(@Value("${elasticjob.zookeeper-url}") String zookeeperUrl, @Value("${elasticjob.group-name}") String group){
//配置zk地址,调度任务的组名
ZookeeperConfiguration zookeeperConfiguration = new ZookeeperConfiguration(zookeeperUrl, group);
zookeeperConfiguration.setSessionTimeoutMilliseconds(100);
CoordinatorRegistryCenter regCenter = new ZookeeperRegistryCenter(zookeeperConfiguration);
return regCenter;
}
}
第三步:定义一个处理程序 简单的
这是一个简单的,实现定时任务的方法,实现SimpleJob这个接口,实现execute 这个方法,那么当该方法被检测感知到的时候,就会按照定时任务配置的来进行执行
ShardingContext参数介绍:
sharding: 如果写3按照默认的方法,那么根据最基本的分片原则,假设你有三个部署,那么每一次的任务会均匀的分配到三台服务器上
shardingParam: 这个参数和sharding对应的如果sharding=3,那么shardingParam 就需要配置三个分片处理的信息比如 0="中国",1="城市",2="人民",那么这三个参数在分配到不同的执行程序上的时候,只会获取其中一个参数.
jobName: 作业名称 FileCustomElasticJob 其实就是执行任务的类的名称
taskId: 作业任务ID.
FileCustomElasticJob@-@0,1,2,3@-@READY@-@192.168.31.51@-@20348 拼上了ip 和分多少片执行
shardingTotalCount: 分片总数.
4呗 0123
jobparam: 很显然,一个正常的参数,可以在指定的时候进行获取
@Component
public class MySimpleJob implements SimpleJob {
//任务调度的方法,每隔一段时间,就会执行
@Override
public void execute(ShardingContext shardingContext) {
//定时任务逻辑
System.out.println("执行的时间:"+new Date());
}
}
//数据流
@Component
public class FileCustomElasticJob implements SimpleJob {
@Autowired
private FileCustomMapper fileCustomMapper;
@Override
public void execute(ShardingContext shardingContext) {
doWork(shardingContext.getShardingParameter());
}
private void doWork(String fileType){
List<FileCustom> fileList = fileCustomMapper.selecByType(fileType);
System.out.println("类型为:"+fileType+",文件,需要备份个数:"+fileList.size());
for(FileCustom fileCustom:fileList){
backUpFile(fileCustom);
}
}
private void backUpFile(FileCustom fileCustom){
try {
//模拟备份动作
TimeUnit.SECONDS.sleep(1);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("执行文件备份====>"+fileCustom);
fileCustomMapper.changeState(fileCustom.getId(),1);
}
}
第四步:定义这个简单程序该如何执行
解释:
registryCenter:已经连接zookeeper的一个注册中心管理类
createJobConfiguration:创建一个Job的配置中心
class-字节码
cron 多久执行一次
shardingTotalCount 分多少片
dataFlowType 是否是数据流的定时任务
如果需要设置更多的东西也是可以加字段的比如我写的JobParam是123可以根据业务更换
iniDataflowElasticJob:在这个类里面初始化好该任务fileDataFlowJob定时任务的执行策略,
连接到哪个注册中心,如何执行,如何分片,分片的参数是啥,是否是数据流指定,等信息,然后返回放到Spring容器里面来进行管理
/**
* Created by wolfcode-lanxw
*/
@Configuration
public class ElasticJobConfig {
@Autowired
private CoordinatorRegistryCenter registryCenter;
@Bean(initMethod = "init")
public SpringJobScheduler iniDataflowElasticJob(FileDataflowJob fileDataflowJob){
SpringJobScheduler springJobScheduler = new SpringJobScheduler(
fileDataflowJob,//业务类
registryCenter,//zookeeper 类
createJobConfiguration//配置类
(FileDataflowJob.class,"0/5 * * * * ?",2,"0=text,1=image,2=radio,3=vedio",true));
return springJobScheduler;
}
private static LiteJobConfiguration createJobConfiguration(final Class<? extends ElasticJob> jobClass,
final String cron,
final int shardingTotalCount,
final String shardingItemParameters,
boolean dataflowType) {
// 定义作业核心配置
JobCoreConfiguration.Builder jobCoreConfigurationBuilder = JobCoreConfiguration.newBuilder(jobClass.getSimpleName(), cron, shardingTotalCount);
if(!StringUtils.isEmpty(shardingItemParameters)){
jobCoreConfigurationBuilder.jobParameter("123").shardingItemParameters(shardingItemParameters); //条件
}
JobTypeConfiguration jobConfig = null;
if(dataflowType){
jobConfig = new DataflowJobConfiguration(jobCoreConfigurationBuilder.build(),jobClass.getCanonicalName(),true);
}else{
// 定义SIMPLE类型配置
jobConfig = new SimpleJobConfiguration(jobCoreConfigurationBuilder.build(), jobClass.getCanonicalName());
}
// 定义Lite作业根配置
LiteJobConfiguration simpleJobRootConfig = LiteJobConfiguration.newBuilder(jobConfig).overwrite(true).build(); //这个是 覆盖之前的配置,现在的配置可以生效
return simpleJobRootConfig;
}
}
二:数据流定时任务和简单的定时任务有啥区别
这是数据流,需要继承的接口,那么区别是什么,区别就是 这里的定时任务,执行分片的时候是在fetchData来执行的,这个方法,是可以返回一个List的那么 processData 就可以处理定时定时任务返回的数据,数据流的定时任务
我能想到的应用场景: 定时任务分片执行,0=redis 进行缓存数据处理,1=mysql 定时任务落盘 2=报表统计,三个任务在三个不同的服务下来执行.
//数据流分布式作业接口.
public interface DataflowJob<T> extends ElasticJob {
/**
* 获取待处理数据.
* @param shardingContext 分片上下文
* @return 待处理的数据集合
*/
List<T> fetchData(ShardingContext shardingContext);
/**
* 处理数据.
* @param shardingContext 分片上下文
* @param data 待处理数据集合
*/
void processData(ShardingContext shardingContext, List<T> data);
}
强化理解 这个分片就意味着,五秒钟内执行一次,分词通过分片的服务器来分发不用的分片参数,不同的服务器,来进行处理,不同的分片参数 下面这张截图一共有四个,第一个只会接受image 那么第二个只会接受radio 这样子依次退推下去,这样做的好处就是,不用担心分布式部署,重复执行的问题
@Component
public class FileCustomElasticJob implements SimpleJob {
@Autowired
private FileCustomMapper fileCustomMapper;
@Override
public void execute(ShardingContext shardingContext) {
doWork(shardingContext.getShardingParameter());
}
private void doWork(String fileType){
List<FileCustom> fileList = fileCustomMapper.selecByType(fileType);
System.out.println("类型为:"+fileType+",文件,需要备份个数:"+fileList.size());
for(FileCustom fileCustom:fileList){
backUpFile(fileCustom);
}
}
private void backUpFile(FileCustom fileCustom){
try {
//模拟备份动作
TimeUnit.SECONDS.sleep(1);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("执行文件备份====>"+fileCustom);
fileCustomMapper.changeState(fileCustom.getId(),1);
}
}