业务场景中经常有一些场景需要使用定时任务,比如:
传统的定时任务实现方案,比如Timer,Quartz等都或多或少存在一些问题:
elastic-job 是由当当网基于quartz 二次开发之后的分布式调度解决方案 , 由两个相对独立的子项目Elastic-Job-Lite和Elastic-Job-Cloud组成 。
ElasticJob-Lite定位是无中心化的分布式定时调度框架,采用zookeeper实现分布式协调,实现任务高可用以及分片。
ElasticJob-Cloud提供资源治理、应用分发以及进程隔离等功能。
ElasticJob-Lite | ElasticJob-Cloud | |
---|---|---|
无中心化 | 是 | 否 |
资源分配 | 不支持 | 支持 |
作业模式 | 常驻 | 常驻 + 瞬时 |
部署依赖 | ZooKeeper | ZooKeeper + Mesos |
实现原理
1. 作业启动
2. 作业执行
缺点
elasticjob是无中心化的,通过ZooKeeper的选举机制选举出主服务器。如果主服务器挂了,会重新选举新的主服务器。
因此elasticjob具有良好的扩展性和可用性,但是使用和运维有一定的复杂。
xxl-job就是一个中心化管理系统,系统主要通过MySQL管理定时任务信息,通过DB锁保证集群分布式调度的一致性。虽然扩展执行器会增大DB的压力,但是实际上大部分公司任务数,执行器并不多。
当到了定时任务的触发时间,就把任务信息从db中拉进内存,对任务执行器发起触发请求。这个任务执行器,既可以是bean、groovy脚本、python脚本等,也可以是外部的http接口。
相比起当当网开源的elastic-job-lite(基于zookeeper作为协调器的“无中心”架构),这种中心化管理的系统轻量级、上手快、易于维护。
2.1.0版本前核心调度模块都是基于quartz框架,2.1.0版本开始自研调度组件,移除quartz依赖 ,使用时间轮调度。
定时执行任务逻辑:
定时任务scheduleThread:不断从db把5秒内要执行的任务读出,立即触发 / 放到时间轮等待触发,并更新trigger_next_time
定时任务ringThread:时间轮实现到点触发任务
当xxl-job应用本身集群部署(实现高可用HA)时,通过mysql悲观锁实现分布式锁(for update语句)
xxl-job添加执行器到任务调度中心有两种方式
(1)客户端执行器自动将名称和机器地址注册到任务调度中心,任务调度中心对外提供注册地址/api用来接受任务执行器注册的相关服务器信息
(2)在任务调度中心手动录入执行器名称和相关的机器地址,使用db表xxl_job_group记录下执行器的信息:执行器AppName、执行器名称title、执行器地址列表address_list(多地址逗号分隔)
执行器集群部署时提供丰富的路由策略,包括:第一个、最后一个、轮询、随机、一致性HASH、最不经常使用、最近最久未使用、故障转移、忙碌转移等;
一般建议指定到单独一台主机上并保证在单机上任务不会并发执行来解决。
1)任务依赖不支持环,只支持DAG;
如:A->B->(C,D)->E 其中CD并行,其余串行
2)下游任务只支持上游所有任务都成功并调度时间到了,才执行任务;
如:JobA只有在Job1,Job2,Job3都执行完,并且时间到了才能执行。
3)不支持有不同调度周期的任务存在依赖关系
如:A->B B的前置任务为A, A的调度周期为每15分钟调度一次, B为每天早上1点调度,该任务不建议分布式调度中心执行。
很难判断前置任务是成功还是失败;建议把A任务拆分为两个任务,一个为B对前置任务A1,一个为每15分钟执行一次(调度时间过滤掉A1)的任务
JobA依赖Job1,Job2,Job3执行,同时JobA3点也会调度执行,在3点左右时,Job3执行完后会执行JobA,同时cron调度也会执行JobA,在这种情况怎么保证JobA只被执行一次。
解决办法:在JobA执行前需要把JobA的状态修改为正在执行中,此时,通过update where jobId = #{jobId} and status=#{未开始执行} 方法执行更新,如果更新记录为1的,任务可以进行执行,如果更新记录为0,抛弃该任务的执行。
条件一:1点钟Job1执行完了,开始找后置任务JobA,JobA是否该执行?怎么判断?
JobA不该执行,前置任务Job2,Job3 都没开始执行,Job1不能执行;
条件二:3点钟Job3执行完了,开始找后置任务JobA,JobA是否该执行?怎么判断?
JobA不该执行,前置任务Job1,Job2,Job3 都执行完了,但是Cron时间还没到,Job1不能执行;
条件三:3点15分调度器开始调度,JobA是否该执行,怎么判断?
JobA该执行,前置任务Job1,Job2,Job3 都执行完了,Cron时间也到了;
判断任务是否执行的逻辑: 如果JobA执行时,需要判断Job1,Job2,Job3是否执行,下面拿Job1为例
假设Job1的历史任务都是正常执行成功的。
情况1: 2019-06-26 00:30:00(today)时,Job1的上一次执行成功时间为2019-06-25:01:00:00 (lastDay),下一次执行时间为:2019-06-26 01:00:00(nextDay).
情况2: 2019-06-26 01:30:00时,Job1的上一次执行成功时间为2019-06-26:01:00:00,下一次执行时间为:2019-06-27 01:00:00.
任务失败应该同时执行带依赖执行和不带依赖执行,由页面配置控制。如果页面配置执行任务有参数,参数需要传递给依赖任务。
参考:
ElasticJob官网文档:http://shardingsphere.apache.org/elasticjob/
Elastic-job 介绍与使用:https://www.jianshu.com/p/4dc449cdeb67
LTS源码地址:https://github.com/ltsopensource/light-task-scheduler
sia-task源码地址:https://gitee.com/mirrors/sia-task?hmsr=aladdin1e6
XXL-JOB源码地址:https://github.com/xuxueli/xxl-job
3千字带你搞懂XXL-JOB任务调度平台:https://baijiahao.baidu.com/s?id=1681035278234207562&wfr=spider&for=pc
XXL-JOB(4) 原理分析:http://www.heartthinkdo.com/?p=3181
分布式任务调度系统xxl-job小结:https://zhuanlan.zhihu.com/p/91862341
xxl问题汇总:https://www.cnblogs.com/smileIce/p/11156412.html
xxl-job原理:https://blog.csdn.net/yanghuangsanguo/article/details/90701592