分布式定时任务|青训营笔记

262 阅读6分钟

这是我参与「第五届青训营 」笔记创作活动的第7天

前言

考虑一种QPS较高的场景,抖音春节的集卡瓜分红包 ,支付宝的集五福瓜分红包,服务端的处理流程整体分为两步。 image.png

  1. 收集每个用户的集卡信息,计算出每个用户瓜分的固定红包金额。
  2. 除夕零点,定时开奖,所有用户收到红包 用户规模亿万级 资金规模亿级 读写QPS百万级

这样的场景需要采用分布式定时任务进行处理 image.png

发展历程

  • windows批处理 image.png
  • window任务计划程序 比如每天12点自动打卡,可以写一个python脚本,抓取页面,查看发送的请求。然后设置为定时任务

image.png

  • Linux命令-CronJob定期清理linux日志

image.png

  • 单机定时任务-java的timer类,Go的Ticker
  • 单机定时任务-ScheduledExecuteorService 不用为每个定时任务都开启一个线程,实现线程复用,但是还是单机可用
public static void main(String[] args) {
    scheduler  = Executors.newScheduledThreadPool(5);
    SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
    scheduler.scheduleAtFixedRate((new Runnable() {
        @Override
        public void run() {
            System.out.println("执行定时任务"+ sdf.format(new Date()));

        }
    }),0,10, TimeUnit.SECONDS );
}

十秒执行一次,结果如下

image.png

  • 任务调度-Quartz

一个由java编写的开源的任务调度框架 www.w3cschool.cn/quartz_doc/…

image.png

  • 分布式定时任务 平台化管理,分布式部署,支持海量数据 需要很多的机器去执行任务

image.png

定时任务是系统为了自动完成特定任务,定时,延时,周期性完成任务调度的过程

分布式定时任务是把这些分散的、可靠性差的定时任务纳入统一的平台,并实现集群管理调度和分布式部署的一种定时任务的管理方式

  • 按触发时机分类:
    • 定时任务:特定时间点,比如今天12:00
    • 延时任务:延时触发,比如一个小时后执行
    • 周期性任务:固定周期时间,或固定频率周期性调度触发,比如每天12点或者每隔3小时

特点

image.png

执行方式

image.png

对于红包场景

单机任务由于任务过多,单机不合适

广播任务所有机器执行的是同一个任务

春节集卡选择的合适的执行方式

image.png

image.png

image.png

实现原理

核心架构

  • 触发器:Trigger,解析任务,生成触发事件
  • 调度器:Scheduler,分配任务,管理任务生命周期
  • 执行器:Executor,获取执行任务单元,执行任务逻辑

除此之外,还有控制台Admin,提供任务管理和干预的功能

image.png

数据流

image.png

功能架构

任务之间的依赖 任务暂停执行就是干预的一种

Trigger需要触发一些任务的执行,所以需要Scanner扫描任务DB,进行触发,然后用消息队列做一些消息的投递,通过消息通知调度器执行

调度器主要是解决如何调度任务。哪些执行器可用被调度等。

真正工作的是执行器

image.png

控制台

基本概念

image.png

  • 任务:job,任务元数据
  • 任务实例:JobInstance,任务运行的实例(一个任务可能在不同时间执行多次,所以对应多个实例)
  • 任务结果:JobResult,任务实例运行的结果(任务可能执行失败,可能执行多次,每次对应一个任务结果)
  • 任务历史:JobHistory,用户可用修改任务信息,任务实例对应的任务元数据可用不同,因而用任务历史存储
任务元数据

任务元数据是用户对任务属性进行定义,

  • 基础信息 Who 任务名字,属于什么业务
  • 调度时机 When 任务什么时间调度
  • 执行行为 What 任务执行的行为,任务代码,做的事情
  • 执行方式 How 单机还是广播,还是分片触发
任务实例

是一个确定的Job的一次运行实例

  • Job_id 和任务元数据建立关联
  • 触发时间 任务实例的执行时间
  • 状态&结果 任务实例当前状态和执行的结果
  • 过程信息 比如进行消息追回

触发器

核心职责

image.png 核心职责: 给定一系列的任务,解析他们的触发规则,在规定的时间点触发任务的调度

设计约束:

  • 需支持大量任务
  • 需支持秒级的调度
  • 周期任务需要多次执行
  • 需保证秒级扫描的高性能,避免资源浪费
方案一

定期扫描+延时消息(腾讯字节方案)

扫描器每10秒扫描一次DB,扫描出最近10分钟将要执行的任务,(扫描出的任务不是立马执行的,就可以用大延时任务的方式)交给Processor,处理后发送延时消息到MQ。

方案2

时间轮(Quartz所用方案)

高效利用线程资源进行批量化调度的一种调度模型。时间轮是一个存储环形队列,底层采用数组实现,数组每个元素存放一个定时任务列表 image.png 目标:遍历任务列表,找出当前时间点需要触发的任务列表

  • 链式存储,每个元素代表一个任务,查询复杂度O(n),修改复杂度O(1) image.png
  • 使用最小堆优化,因为我们不需要知道所有任务的执行时间,只需要知道最近的要执行的任务。按照任务的执行时间进行排序,查询复杂度O(1),修改复杂度O(logn).当任务增加时,堆是无限扩展的,所以不符合 image.png
  • 时间轮:查询和修改复杂度都是O(1),按照时间取对应的槽位

image.png 因为秒轮时间刻度可能不够,所以可用使用多级时间轮

image.png

高可用性

核心问题

  • 不同业务之间,任务的调度互相影响怎么办
  • 负责扫描和触发的机器挂了怎么办

解法思路

  • 存储上,不同国别、业务做资源隔离
  • 运行时、不同国别、业务分开执行官
  • 部署时,采用多机房集群化部署,避免单节点故障,通过数据库锁或分布式锁保证任务只被触发一次。

高可用-问题引出

image.png

高可用-数据库行锁模式

image.png

高可用-分布式锁模式

image.png 性能较高,也是多家公司使用的方案

调度器

  • 资源来源

image.png

  • 资源调度

节点选择

image.png

  • 任务执行

image.png 高级特性-任务编排 使用有向无环图DAG进行可视化任务编排

image.png 高级特性-故障转移

确保部分执行单元任务失败时,任务最终成功 image.png

分片任务基于一致性hash策略分发任务,当某个executor异常时,调度器会将任务分发到其他executor上

调度器-高可用

调度器可用集群部署,做到完全的无状态,靠消息队列的重试机制保障任务一定会被调度到

image.png

执行器

image.png 注册 调度 回调 状态上报类似于心跳检测

业务应用

image.png

image.png

消息队列不做存储,所以可控性较低。 image.png