这是我参与「第五届青训营 」笔记创作活动的第12天
1.分布式定时任务
开奖环节技术方案设计:用户规模(亿级)、资金规模(亿级)、读写QPS(百万级)
自动化+定时执行+海量数据+高效稳定-->分布式定时任务
单机定时任务
每隔五分钟定时刷新本地缓存数据
// Java单机定时任务
public static void main(String[] args)throws ParseException{
Timer.timer = new Timer();
timer.schedule(new TimerTask(){
@Override
public void run(){
SynclocalCache();
},5000,5*60*1000);
}
// Go单机定时任务
func main(){
ticker := time.NewTicker(5 * time.Minute)
for{
select{
case <- ticker.C:
SyncLocalCache()
}
}
}
// 定时执行多个任务
private static ScheduledExecutorService scheduler;
public stiatic void main(String[] args) throw Exception{
shceduler = Executors.newScheduledThreadPool(5);
scheduler.scheduleAtFixedRate(((
new Runnable(){
@Override
public void run(){
DoSomething();
}
})),0,300,TimeUnit.SECONDS);
}
使用ScheduledExecutorService使得定时任务具有线程池的功能,但是仍然只是单机可用的
分布式定时任务
- 平台化管理
- 分布式部署
- 支持海量数据
定义
- 定时任务是指系统为了自动完成特定任务,实时、延时、周期性完成任务调度的过程。
- 分布式定时任务是把分散的、可靠性差的定时任务纳入统一的平台,并实现集群管理调度和分布式部署的一种定时任务的管理方式。
- 按照触发时机分类:
-
- 定时任务:特定时间触发
-
- 延时任务:延时触发
-
- 周期任务:固定周期时间,或固定频率周期调度触发
执行模式
- 单机任务:随机触发一台机器执行任务
- 广播任务:广播到所有机器上执行同一个任务,比如一起清理日志
- Map任务:一个任务分出多个子任务
- MapReduce任务:Map任务的基础上,对所有子任务的结果做汇总计算
业内定时任务框架
- Xxl-job(美团点评):开源
- SchedulerX(阿里巴巴):非开源
- TCT(腾讯):非开源
- Elastic-job(当当网):开源
2.分布式定时任务实现原理
分布式定时任务核心要解决触发、调度、执行三个关键问题
graph LR
A(Trigger)
B(Scheduler)
C(Executor)
D(Admin)
A-->B
B-->C
D-->A
D-->B
D-->C
- 触发器(Trigger):解析任务,生成触发事件
- 调度器(Scheduler):分配任务,管理任务生命周期
- 执行器(Executor):获取任务单元,执行任务逻辑
除此之外,还提供了控制台来进行管理等。
功能架构
基本概念
控制台:
- 任务:Job,任务元数据
- 任务实例:JobInstance
- 任务结果:JobResult
- 任务历史:JobHistory,用户可以修改任务信息,任务实例对应的任务元数据可以不同,因而使用任务历史存储。
graph TD
Job --> 基础信息
Job --> 调度时机
Job --> 执行行为
Job --> 执行方式
graph TD
JobInstance --> Job_id
JobInstance --> 触发时间
JobInstance --> 状态&结果
JobInstance --> 过程信息
触发器
核心职责
给定一系列任务,解析触发规则,在规定的时间点触发任务调度
设计约束
- 支持大量任务
- 需要支持秒级的调度
- 周期任务需要多次执行
- 需要保证秒级扫描的高性能,避免造成资源浪费
方案1(腾讯、字节方案)
定期扫描+延时消息
扫描器每隔一段时间扫描一次DB,扫描出的任务送至processor,processor发送延时消息设置执行定时,并且对DB状态进行修改。
方案2(Quartz方案)
时间轮方案
1.使用链表存储任务,从中找出当前时间点需要触发的任务列表 (查询复杂度O(N),修改复杂度O(1))
2.使用最小堆存储任务,按执行时间排序,每个节点存储同执行时间的任务列表 (查询复杂度O(1),修改复杂度O(logn))
3.时间轮存储任务,类似于时钟的运行方式,时钟刻度代表同一时刻的执行任务(链表决定先后),使用count来记录大于时轮范围的数据,每次经过时自减。
4.多级时间轮任务,类似于索引的方式,按照时轮-秒轮-分轮的方式进行插入。
高可用问题
单Trigger模式:会有单点故障
graph TD
Trigger1
Trigger集群模式:可以避免单点故障,但是也需要避免同一任务多次触发
graph TD
Trigger1
Trigger2
...
TriggerN
行锁模式:在触发调度之前,更新数据库中JobInstance的状态,成功抢到行锁的机器可以修改状态
分布式锁模式:在触发调度之前,尝试抢占分布式锁,可使用Redis锁或Zookeeper锁,相对于行锁模式性能相对较高。
调度器
资源来源
- 业务系统提供机器资源,任务执行逻辑与业务系统共用一份资源
- 定时任务平台提供机器资源
节点选择
- 随机节点执行:选择集群中一个可用的执行节点执行,定时对账常用
- 广播执行:集群中所有执行节点分发任务并执行,批量运维常用。
- 分片执行:自定义逻辑拆分,分发到集群的不同节点,海量日志统计常用。
graph LR
任务-->任务分片
任务分片-->Executor1
任务分片-->Executor2
任务分片-->ExecutorN
Executor1-->业务数据
Executor2-->业务数据
ExecutorN-->业务数据
任务编排
使用ui进行用户界面的自定义编排
故障转移
确保部分执行单元失败时,任务最终成功
graph LR
Scheduler-->Executor1
Scheduler-->Executor2
Scheduler-->ExecutorN
Executor1-->调度失败
Executor2-->调度成功-->结束
Scheduler在分配执行单元时使用一致性哈希的策略:
普通的分布式Hash算法即为对结果模服务器台数取余,保证了服务与服务器映射关系的一致性,但是遇到服务器的增减时所有节点的哈希值都可能发生改变。为了解决这一情况,使用环式存储结构,将模变为一个足够大的值,在保证服务器节点不冲突的情况下先对服务器取模运算放置在环上,然后对各个服务取模哈希,得到的地址沿着顺时针方向遇到的第一个服务器节点即为映射的服务器,这样在增减服务器时只需要改变一小部分的映射关系。
高可用
调度器本身是无状态的服务,可以进行集群部署。
执行器
总结
本节课程主要讲解了分布式定时任务的概念和机制,讲解了几种常见框架的底层实现架构,介绍了这一技术的具体演变过程,并且引申了这一技术正在应用的领域。