xxl-job调度平台之任务触发

在上一篇《xxl-job调度平台之任务执行》中,我们知道了任务的一次执行流程。今天,我们一起来看看,任务是如何被触发的。

本文中源码,来自xxl-job 2.4.0版本

一 手动触发

在任务页面点击【执行一次】按钮,将立即触发执行。 对应后端接口如下:调用JobTriggerPoolHelper.trigger(),failRetryCount取-1。

二 定时触发

配置cron表达式,将任务设置为启动后,将由调度平台周期性触发执行。 在调度平台启动时,启动了调度线程。

java 复制代码
JobScheduleHelper.getInstance().start();

2.1 JobScheduleHelper类

该类启动了两个线程:scheduleThread、ringThread,前者循环周期长,后者循环周期短。

主要属性如下:

java 复制代码
// 向后预读取时间,5秒
public static final long PRE_READ_MS = 5000;    // pre read
// 处理已过期或5秒内将执行的任务,这些任务是从DB中查询的
private Thread scheduleThread;
// 整秒执行一次,处理ringData
private Thread ringThread;

private volatile boolean scheduleThreadToStop = false;
private volatile boolean ringThreadToStop = false;

// 时间轮,key是秒数(60以内的整数),value是jobId集合
private volatile static Map<Integer, List<Integer>> ringData = new ConcurrentHashMap<>();

2.2 scheduleThread线程

  • 整秒处运行,两次间隔最多5秒,5秒内最多执行一次;
  • 从库表xxl_job_info中查询trigger_next_time <= now + 5且启动了的任务;这些任务可能已过点,也可能未过点。
  • 如果任务已过点5秒以上时,根据"调度过期策略",选择忽略或立即执行一次;
  • 如果任务过点在5秒内时,直接触发一下,如果下次执行时间在之后的5秒内,则将任务添加到ringData
  • 如果任务将在5秒内到期,则将任务添加到ringData
  • 根据cron计算下次执行时间,更新triggerLastTime、triggerNextTime属性。

来看看具体细节:

  • 两次执行间最大间隔5秒
  • 查询XxlJobInfo时,采用limit限制数量,默认为6000条。
  • 当调度平台集群部署时,为了防止节点间并发,使用select for update加表行锁;加锁成功才继续执行;
  • 在查询时,预读取了5秒内到期的任务;
  • 假设新建任务,从0点起每12小时执行一次,trigger_next_time默认为0;00:20时将它启动:
    • 如果调度过期策略设置为"忽略",那得等到12点才首次执行;
    • 如果调度过期策略设置为"立即执行一次",将在几秒内完成首次执行;
  • 5秒内将到期的任务,不能立即执行;放入ringData中,由ringThread处理;
  • 调度类型分为cron、固定速率两种,前者依赖于CronExpression计算下次执行时间。

2.3 ringThread线程

  • 整秒处运行,两次间隔最多1秒,1秒内最多执行一次;
  • 获取当前时间的秒数N,则N >= 0 && N < 60,从ringItemData中查找N映射的jobId集合,然后一个个触发执行。

来看看具体实现:

  • 校准为整秒时执行一次
  • 从ringData中查找当前秒数,及过期1秒的任务
  • 遍历触发

2.4 时间轮

其实,ringData的Map结构,就是时间轮(图片来自网络):

  • tickMs=1秒,
  • wheelSize=60,在0-59的格子中放入这一秒到期的所有任务。
  • currentTime即当前时间的秒数,只是xxl-job向前增加了一个跨度;假设现在是第31秒,则获取31、30两个格子的任务。
java 复制代码
// 当前秒数
int nowSecond = Calendar.getInstance().get(Calendar.SECOND)
  • tickMs(基本时间跨度) :时间轮由多个时间格组成,每个时间格代表当前时间轮的基本时间跨度(tickMs)。
  • wheelSize(时间单位个数) :时间轮的时间格个数是固定的,可用(wheelSize)来表示;
  • currentTime(当前所处时间) :时间轮还有一个表盘指针(currentTime),用来表示时间轮当前所处的时间,currentTime 是 tickMs 的整数倍。currentTime 当前指向的时间格,表示刚好到期,需要处理此时间格所对应的所有任务。

三 总结

  • 两个线程,一个线程查表,获取将到期任务,放入时间轮中,交由另一个线程来处理;这种设计在定时任务的实现中常被采用;
  • 采用DB表行锁,在调度平台多节点间,避免并发调度;
  • 调度过期策略,是有业务价值的;
  • 任务执行可能出现一个周期的延迟
    • 如调度平台节点A,将5秒内到期的任务放入ringData,然后宕机了,且TriggerNextTime已被修改;
    • 节点B紧接着执行时,得等到下一个到期时间,才能查询到这个任务;
    • 举例:某任务(周期1小时)20:55:00到期,20:54:56时被节点A查到,放入ringData,更新triggerLastTime为21:55:00,但20:54:58时节点A宕机了。该任务只能等到21:55:00才被执行。
相关推荐
Mandy的名字被占用了7 小时前
晨风AI+知识付费系统|学练考全闭环,重构教育变现新模式
人工智能·后端
why技术7 小时前
分享一套我一直在使用的 AICoding 组合拳,小而美的典范。
前端·后端·ai编程
架构源启7 小时前
文档接入与智能解析:基于 Spring AI 1.1.x 的多格式解析、版面理解与结构化抽取
java·人工智能·spring
GetcharZp8 小时前
SRS实战:一个开源流媒体服务,支持6大协议,80ms低延迟,轻松搭建自己的直播平台
后端
Csvn8 小时前
📊 SQL 入门 Day 7:子查询 — 查询中的查询
后端·sql
大模型码小白11 小时前
JAVA 集合框架进阶:List 与 Set 的深度解析与实战
java·开发语言·人工智能·windows·语言模型·list·ai编程
IT_陈寒11 小时前
为什么我的JavaScript异步代码总是不按顺序执行?
前端·人工智能·后端
星栈11 小时前
Node 框架怎么选?Express、Koa、Egg、NestJS 场景化选型指南
后端·node.js
名字还没想好☜12 小时前
Go 的 time.Ticker 陷阱:定时任务里被忽略的内存泄漏与正确关闭
java·数据库·golang·go·定时器
锋行天下12 小时前
打造企业内部知识库系统RAG全栈项目
前端·后端·架构