这是我参加「第五届青训营 」伴学笔记创作活动的第 10 天。今天学习的是分布式定时任务的核心架构。
重点内容
- 定时任务的类型
- 分布式定时任务的核心架构
主要知识点
一、分布式定时任务出现的背景
在项目开发的实际场景中,我们常常会遇到类似需要通过转发或集卡等方式参与抽奖,在某一个时间点进行开奖的应用场景。由此需要引入定时任务的概念:
- 定时任务是指系统为了自动完成特定任务,实时、延时、周期性完成任务调度的过程。
而由于在一些定时抽奖的场景下,服务器需要处理大量数据,为了满足这些要求, 分布式定时任务应运而生:
- 分布式定时任务是在定时任务的基础上,把分散的、可靠性差的定时任务纳入统一的平台,实现集群调度和分布式部署的一种定时任务管理机制。
二、分布式定时任务的发展历程
1、单机定时任务
-
操作系统自带的定时任务:在一些操作系统中,开发者为用户提供了一些方法,如windows系统中可以通过编写.bat文件实现某些命令的定时执行,linux可以利用CronJob定时执行命令。这些方法的优缺点都十分直观:
优点:使用简单,稳定可靠
缺点:只能控制单台机器,且不能跨操作系统使用,难以移植
-
代码实现定时任务:一些编程语言也提供了实现定时任务的方法,如java中的Timer,Go中的Ticker。
优点:可以靠平台使用
缺点:实现定时任务需要新建线程,造成性能浪费;仍然只能控制单台机器
-
ScheduledExecutorService:ScheduledExecutorService是java中的一个定时任务类。与Timer相比,它拥有一个自我管理的线程池,可以有效地提高处理效率。
优点:拥有线程池,在Timer的基础上提高了代码执行的性能,同时支持循环任务与延时任务。
缺点:只有单机可用
-
Quarts:一个功能强大的单任务控制框架,拥有极强的任务调度能力。
优点:功能强大,对单任务的控制能力达到极致,对定时任务的实现结构很成熟
缺点:没有负载均衡机制,不适应分布式环境
2、分布式定时任务
随着互联网用户的井喷式增长,定时任务需要满足数千万甚至上亿用户的需求,此时单机定时任务显然不能满足这样的需要。为了处理大量并发的定时任务请求,分布式定时任务具有以下特点:
- 分布式定时任务是基于平台的,管理方式为平台化管理
- 分布式定时任务支持分布式部署,可以让多台机器参与定时任务的处理
- 能够处理海量数据,满足当前项目的需求
分布式定时任务有多种执行方式,包括单机任务,广播任务,Map任务,MapReduce任务,其中主要解释Map任务与MapReduce任务:
- Map任务:一个任务可分为多个子任务,每个子任务负责一部分计算。适用于计算量大,单机任务无法满足要求的任务
- MapReduce任务:在Map任务的基础上,可以对所有子任务的结果作汇总。适用于计算量大,结果需要汇总的任务
三、分布式定时任务的核心架构
1、核心架构
分布式定时任务核心要解决触发、调度、执行三个关键问题,为此引入了
- 触发器:解析任务,生成触发事件
- 调度器:分配任务,管理任务生命周期
- 执行器:获取并执行任务单元,执行任务逻辑
- 控制台:提供外部程序员对任务的管理和干预的功能
分布式定时任务的具体流程如下图所示
2、功能架构
3、基本概念
-
任务元数据(Job):任务元数据是用户对任务属性的定义,包括基础信息(谁要执行任务),调度时机(合适需要结构),执行行为(任务需要什么),执行方式(如何执行任务)四个部分。
-
任务实例(JobInstance):任务实例是一个确定的Job的一次运行实例。
-
任务结果(JobResult):任务实例执行的结果。
-
任务历史(JobHistory):分布式定时任务平台执行任务实例的历史记录。
4、触发器
在分布式定时任务中,触发器需要给定一系列任务,解析它们的触发规则,在规定的时间点触发任务的调度。触发器需要满足以下约束
- 支持大量任务
- 支持秒级调度
- 周期任务需要多次执行
- 保证秒级扫描的高性能,避免资源浪费
方案:定时扫描+延时消息
这一方案被腾讯与字节采用,该方案的思路是
- 在触发器中实现一个扫描器(Scanner),对DB中的任务进行定时扫描,将最近一段时间需要执行的任务扫描出来,交付给触发器的执行部分(Processor);
- 在执行部分,触发器对任务进行分析,将任务的信息通过消息队列中的延时消息发送到调度器当中,同时执行修改操作,将已经发往调度器的任务删除或者做标记,避免反复读取同一个任务。
- 当调度器执行完任务后,若任务需要周期性执行,再由调度器在DB中添加该任务。
方案:时间轮
触发器也可以通过多级时间轮的方式存储定时任务,时间轮的结构难以用语言表达清楚,引入一张图片
假设任务2需要在2:30:29执行,此时任务二会被存在时论的下标为2的地址处,程序通过读取时间,将时论指针位置与当前时间同步,当到达2点时,指针指向2,任务二就会被读取并根据自身携带的调度时机存放到分轮的对应位置(根据假设为30)。此时程序依然会像处理时轮指针那样根据读取到的时间中的分钟数调整分轮指针的位置,直至读取到任务二并放入秒轮,秒轮也依照相同的操作直至任务二执行。
数据库行锁模式
无论我们选取何种方案执行触发器,在整个分布式定时任务系统平台中必然存在有多个触发器组成的触发器集群。此时我们需要考虑到一个问题:当多个触发器在同一时刻请求触发调度时该如何确定进入调度的次序?
这里我们可以引用类似数据库行锁的方法,在触发调度前更新数据库中任务实例的状态,只有成功抢锁才能触发调度。然而由于数据库本身性能的问题,这个方法效率很低,不推荐使用。
分布式锁模式
为了解决效率问题,人们提出了分布式锁。分布式锁即在触发调度前,各个触发前尝试抢占分布式锁,一般采用的时Redis锁与Zookeeper锁。由于这个方案性能更高,因此被大多数企业采用
5、调度器
调度器需要解决以下问题:
- 资源从哪里来
- 资源如何分配
- 任务如何执行
资源来源
目前调度器的资源来源有两种
-
业务系统提供
优点:任务执行逻辑与业务逻辑共用一份资源,利用率高
缺点:更容易发生定时脚本影响在线服务的事故;不能由定时任务平台控制实现扩缩容
-
定时任务平台提供
优点:任务执行逻辑与业务逻辑互相独立,避免干扰;支持扩缩容
缺点:资源消耗大;需要额外为定时服务平台申请接口调用权限
资源调度
调度器获取资源后,需要对获取到的机器资源进行调度,目前常见的调度方法有三种
-
随机节点执行:选择集群中一个可用的执行节点执行调度任务。适用于定时对账等类似场景。
-
广播执行:在集群中广播分发任务,所有的执行节点共同执行。适用于批量运维等场合。
-
分片执行:按照用户自定义分片逻辑进行拆分,分发到不同节点中并行执行。这种方法能够很快速的处理复杂任务,提升资源利用效率。适用于海量日志统计等场景。
6、执行器
在分布式定时任务系统中,执行器是真正负责执行任务的模块,它负责将自己注册到服务中心,处理调度器发送的调度请求并根据需要输出日志及回调请求,同时依据当前状态进行状态上报。
总结
本节课程主要学习了定时任务及分布式定时任务的概念,对分布式定时任务产生了一个较为全面宏观的认识。