这是我参与「第五届青训营 」伴学笔记创作活动的第 16 天
前言
本文主要介绍分布式定时任务的实现原理、分布式定时任务的架构以及一些核心的的概念等。
核心架构
分布式定时任务核心要解决触发、调度、执行三个关键问题。
触发就是某个定时任务如何在某个时间点会被准时准点的被调度执行。
调度就是在众多机器中,比如10万台、100万台机器,如何协调任务的调度。
执行就是机器在拿到任务后,如何把任务给执行好、如何做好故障恢复。
基于以上3个问题,引入4个组件来解决处理这些问题。
- 触发器:
Trigger,解析任务,生成触发事件 - 调度器:
Scheduler,分配任务,管理任务生命周期 - 执行器:
Executor,获取执行任务单元,执行任务逻辑 - 控制台:
Admin,提供任务管理和干预的功能。
数据流
定时任务由任务创建、任务执行组成,这两部分的执行流程如下:
功能架构
刚才提到分布式定时任务由4个组件组成,那么这4个组件的各自的功能如下:
业务模型
下面是定时任务的E-R图:
- 任务元数据:
Job,任务元数据 - 任务实例:
JobInstance,任务运行的实例 - 任务结果:
JobResult,任务实例运行的结果 - 任务历史:
JobHistory,用户可以修改任务信息,任务实例对应的任务元数据可以不同,因而使用任务历史存储
任务元数据
任务元数据(Job)是用户对任务属性定义,包括:基础信息(Who)、调度时机(When)、执行行为(What)、执行方式(How)等。
任务状态的流转图如下:
任务实例
任务实例(JobInstance)是一个确定的Job的一次运行实例,包括:job_id、触发时间、状态&结果、过程信息。
job_id:与任务进行关联,表示属于什么任务的实例。
触发时间:任务的预期执行时间。
触发器
核心职责
核心职责:给定一系列任务,解析他们的触发规则,在规定的时间触发任务的调度。
设计约束:
- 需支持大量任务
- 需支持秒级的调度
- 周期任务需要多次执行
- 需保证秒级扫描的高性能,并避免资源浪费
下面来看下,为达到这种目的有哪些方案呢?
方案一
定期扫描+延时消息(腾讯、字节方案)
首先有个扫描器,定时扫描数据库,把扫描的结果丢给处理器去执行。
方案二
时间轮(Quart所有方案)
时间轮是一种高效利用线程资源进行批量调度的一种调度模型。时间轮是一个存储环形队列,底层采用数组实现,数组中的每个元素可以存放一个定时任务列表。
假如我们要实现这样一个需求:遍历任务列表,从中找出当前时间点需触发的任务列表。
如果用列表存储任务,每个元素代表一个任务,那么查询复杂度为O(n),修改复杂度O(1)。
如果用最小堆来存储任务,按执行时间排序,每个节点存储同执行时间任务列表,查询复杂度为O(1),修改复杂度为O(logn)。
如果使用时间轮存储任务,每个节点执行时间任务列表,查询复杂度O(1),修改复杂度为O(1)。
时间轮可以有多个,比如年时间轮、月时间轮、日时间轮、时分秒时间轮等,多个时间轮可以进行组合。
高可用
我们可以存在这样的疑问:
- 不同业务之间,任务的调度相互影响怎么办?
- 负责扫描和触发的机器挂了怎么办?
解决思路:
- 存储上,不同国别、业务做资源隔离
- 运行时,不同国别、业务分开执行
- 部署时,采用多机房集群化部署,避免单点故障,通过数据库锁或分布式锁保证任务只被触发一次
集群化部署如何保证任务只被执行一次?
- 数据库行锁模式
- 在触发调度之前,更新数据库中JobInstance的状态,成功抢锁的才会触发调度。
- 分布式锁模式
- 在触发调度之前,尝试抢占分布式锁,可使用Redis或Zookeeper锁
调度器
调度器需要解决的核心问题为:
- 资源来源:这些资源是从哪里来的
- 资源调度:如何对资源进行集中的调度
- 任务执行:有了资源和调度,如何执行用户提交的任务
资源来源
资源来源有两种:业务系统提供机器资源、定时任务平台提供机器资源。
业务系统
业务系统提供机器资源,使用该方案的公司:
- 阿里、美团、字节等
优点:
- 任务执行逻辑与业务系统共用同一份资源,利用率更高
缺点:
- 更容易发生定时任务脚本影响在线服务的事故
- 不能由定时任务平台控制扩缩容
定时任务平台
定时任务平台提供机器资源,使用该方案的公司:字节等。
优点:
- 任务执行逻辑与业务系统提供的在线服务隔离,避免相互影响
- 可以支持优雅地扩缩容
缺点:
- 消耗更多机器资源
- 需要额外为定时任务平台申请接口调用权限,而不能直接继承业务系统的权限
节点选择
在资源调度器进行调度的时候,是如何进行调度的呢?是有多种方案可以选择的:
- 随机节点执行
- 选择集群中一个可用的执行节点执行调度任务,适用场景:定时对账。
- 广播执行
- 在集群中所有的执行节点分发调度任务并执行,适用场景:批量运维。
- 分片执行
- 按照用户自定义分片逻辑进行拆分,分发到集群中不同节点并行执行,提升资源利用率,适用场景:海量日志统计。
任务分片
通过任务分片来提高任务执行的效率和资源的利用率。
任务编排
使用有向无环图DAG(Directed Acyclic Graph)进行可视化任务编排。
故障转移
故障转移:确保部分执行单元任务失败时,任务最终成功。
分片任务基于一致性hash策略分发任务,当某Executor异常时,调度器会将任务分发到其他Executor上。
高可用
调度器可以集群部署,做到完全的无状态,靠消息队列的重试机制保障任务一定会被调度。
执行器
执行器是最后真正执行任务的一个组件,基于注册中心,可以做到执行器的扩缩容。