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才被执行。
相关推荐
向前看-2 小时前
验证码机制
前端·后端
xlsw_2 小时前
java全栈day20--Web后端实战(Mybatis基础2)
java·开发语言·mybatis
神仙别闹3 小时前
基于java的改良版超级玛丽小游戏
java
黄油饼卷咖喱鸡就味增汤拌孜然羊肉炒饭4 小时前
SpringBoot如何实现缓存预热?
java·spring boot·spring·缓存·程序员
暮湫4 小时前
泛型(2)
java
超爱吃士力架4 小时前
邀请逻辑
java·linux·后端
南宫生4 小时前
力扣-图论-17【算法学习day.67】
java·学习·算法·leetcode·图论
转码的小石4 小时前
12/21java基础
java
李小白664 小时前
Spring MVC(上)
java·spring·mvc
GoodStudyAndDayDayUp5 小时前
IDEA能够从mapper跳转到xml的插件
xml·java·intellij-idea