一直以来我都有这样一个疑问,定时任务里面的触发器底层到底是怎样实现的?
直到我百度到了时间轮算法这个东西,恍然大悟。
时间轮算法的思路
时间轮算法的灵感应该来源于手表,在理解时间轮算法的过程中我们也可以用手表的逻辑去思考。时间轮包含三个部分,时间格、任务列表和模拟指针,时间轮的结构如下图所示。
bucket时间刻度,以数组的形式存储,时间刻度的数量可以自定义;indicator是模拟指针,模拟指针负责按照一定的时间频率在时间刻度上面移动,每次向下移动一格;时间刻度本质上是一个链表,链表上维护了需要在这个时间刻度对应时间上需要执行的诸多任务,当模拟指针移动到时间刻度上时,这些任务会被触发执行。假设当前有一个时间轮,时间刻度的数量为10,模拟指针每1s移动一格,当前有一个延时任务需要在6s之后执行,那么这个任务就会被放到第6个时间刻度的任务列表里。
增加round圈数属性的时间轮
上面的时间轮我们很快会发现一格问题,能处理的时间长度取决于时间刻度的数量和模拟指针的频率,时间刻度的数量不能无限增大,而模拟指针的频率决定了时间轮的精度。所以这里引入圈数的概念。假设当前有一个时间轮,时间刻度的数量为10,模拟指针每1s移动一格,当前有一个延时任务需要在32s之后执行,那么这个任务就会被放到第三圈的第2个时间刻度的任务列表里。
如何实现固定频率和CRON表达式
定时任务存放在时间轮上具体哪个位置是基于延时计算的,对于固定频率的定时任务和CRON表达式的定时任务,我们所知道的是任务需要执行的目标时间,用目标时间减去当前时间就能得到延时时间,再按照时间轮的计算方式存放任务。
这只是我们的一个基础思路,为了提高任务调度的效率和精度,还需要更多高端的操作,感兴趣可以撸一下netty、dubbo和powerjob的源码,看看这些优秀的项目是如何实现的。
TP链接:
netty时间轮源码解读:https://zhuanlan.zhihu.com/p/32906730
dubbo时间轮源码:https://www.jianshu.com/p/2093fb93fe19