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

一 手动触发

在任务页面点击【执行一次】按钮,将立即触发执行。 

 对应后端接口如下:调用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才被执行。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值