- 这里描述下我们项目中如何使用elastic-job在分布式环境中进行定时调度。
- 主要功能包括:创建定时任务,激活暂停删除定时任务
- 通知不同的节点进行激活暂停任务(借助zookeeper)
关键类
- ZjobService
- 维护一个 jobMap < String, JobManager > ,用来维护所有的任务极其状态。
- 暴露出新建任务startSimpleJob(),暂停任务,启动任务的方法
- JobManager:包装JobScheduler,同时记录运行状态
- JobScheduler:elastic-job原生用于启动停止任务的类
微服务启动
- 监听zk上 /notifyJob-start 状态变化
- 如果发生变化,则取最新 jobClass 和 cron
- 启动、暂停或者更新 elastic-job 上的任务
- 变化后更新 jobMap中的JobManager
- 从缓存中读取所有的任务,放入到 jobMap 中
- 根据任务的状态,到elastic-job启动相应的任务。
新建任务
- 获取jobname(用户名、微服务、功能名、任务名),运行周期
zJobService.startSimpleJob( jobName, TimingReportTask.class, excutionCycle, true );
启动job
TimingReportTask
- TimingReportTask 任务继承 AbstractSimpleElasticJob
class AbstractSimpleElasticJob{
public AbstractSimpleElasticJob(){}
public abstract void process(JobExecutionMultipleShardingContext var1);
}
- JobExecutionMultipleShardingContext 中可以获取到调度的任务信息,分片信息等
zJobService.startSimpleJob()
- 如果在 jobMap 中找不到任务,则:
- 获取 JobScheduler 。
- 在zk上设置任务需要调用的class(这里是 TimingReportTask )、设置调用的周期 cron
- 初始化 JobScheduler 等待调度。
- 将 JobScheduler 用 JobManager 包装,管理激活暂停状态
//初始化 JobScheduler 等待调度。
JobCoreConfiguration coreConfig = JobCoreConfiguration.newBuilder(taskName, cron, 2).build();
SimpleJobConfiguration simpleJobConfig = new SimpleJobConfiguration(coreConfig, TimingReportTask.class.getCanonicalName());
JobScheduler jobScheduler = new JobScheduler(zookeeperRegistryCenter, LiteJobConfiguration.newBuilder(simpleJobConfig).build());
- 如果在jobMap中找到任务,则:
- 更新周期,调用类等信息
- 修改zk上 /notifyJob-start 的任务状态,其他节点监听到变化后做相应的事
激活暂停任务
- 修改zk上 /notifyJob-start 的任务状态
- 节点(包括当前节点)监听到后更改各自任务的运行状态
- 将JobScheduler暂停
- 将jobMap中对应任务JobManager的状态修改
动态添加任务:https://www.cnblogs.com/f-zhao/p/6768842.html
动态添加任务的核心,通过监听zk实现:https://www.jianshu.com/p/a9458887739d
zk中的结构
- 我们项目中用于保存任务信息的相关节点,主要用于通知不同的运行节点,保证不同的节点状态一致(借助zookeeper)
//我们项目中用于存储任务状态,广播任务状态的zk节点
- clustertimer-job
- jobname
- config
- cron //任务运行周期
- jobClass //任务调用的类
- jobShardingStrategyClass //分片策略
- shardingTotalCount //分片数量
- notifyJob-start //存放任务的状态,借助zk的监听保证各个节点上的任务状态一致
- 模块
- jobname
- elastic-job维护的zookeeper节点(每个版本的elastic-job的zk结构都有点不同)
原理
http://elasticjob.io/docs/elastic-job-lite/03-design/lite-design/
- zk首先启动
- 服务器a启动到zk上注册成为leader,成为leader
- 服务器b启动到zk上注册成为leader,失败
- 服务器a设置分片标记(下次任务执行期重新分片)
- 服务器b设置分片标记(已经设置过,将不再持久化)
- 服务器a注册定时任务信息
- 服务器b注册定时任务信息
- a,b在定时任务信息节点下注册各自的服务器信息
- 等待调度
调度
- 在每次调度前会检查分片标记,如果有必要重新分片,则由leader进行重新分片
- 运行过程中只会标记分片状态,不会重新分片。分片仅可能发生在下次任务触发前。
- 分片会依据执行节点的IP进行(所以一个ip上只有一个节点),保证分片结果不会产生较大波动。
- 分片结束后各个服务调度任务,会去zk上检测分配给自己的分片信息。
- 当机器数量超出了分片数后,有的机器就会得不到分片,就没有调度的机会。当节点发生变化后,将触发了重新分片。
- 每个节点每次需要执行任务时,会检查是否需要进行重新分片;当前节点是否有分片,有分片才会调度执行任务
分片策略
- AverageAllocationJobShardingStrategy:基于平均分配算法的分片策略。
- 如果有3台作业节点,分成10片,则每台作业节点分到的分片是:1=[0,1,2,9], 2=[3,4,5], 3=[6,7,8]。多余的依次分配给序号最小的节点
- OdevitySortByNameJobShardingStrategy:根据作业名的哈希值奇偶数决定IP升降序算法的分片策略。
- 如果有3台作业节点, 分成2片, 作业名称的哈希值为奇数, 则每台作业节点分到的分片是: 1=[ ], 2=[1], 3=[0].
- RotateServerByNameJobShardingStrategy:根据作业名的哈希值对作业节点列表进行轮转的分片策略。
- 如果有 3 台作业节点,顺序为 [0, 1, 2],如果作业名的哈希值根据作业分片总数取模为 1, 作业节点顺序变为 [1, 2, 0]。
分片时机:
- 有节点发生变化(如果下线的是leader节点,那么先选举然后触发分片算法的执行)
- 分片数量发生变化
总结
- elasticjob负责帮我们调度和分片
- 所以我们自己维护了一组zk节点,用于保证不同节点的状态一致性