PHP怎样写延时队列(定时器)

背景

PHP没有定时器,依托的都是crontab这样的系统工具,也没有go中defer这样的延时方法,本文介绍几种PHP写延时队列的几种姿势。

延时队列的定义

普通的队列是先进先出,但是延时队列并不是,而是加上了时间这一权重。希望到达时间点的先执行。

从某种意义上来讲,延迟队列的结构并不像一个队列,而更像是一种以时间为权重的有序堆结构。

Hash

将key使用一个唯一标识,保证每个任务都不重复,也方便删除,然后value中添加需要调用的函数名和时间戳,以及参数。
没秒进行遍历,然后将时间到的取出来执行,再删除。

入队:hSet key:uuid value:{timestamp,function,param}
出队:timestamp > now do function(param)

问题很明显,需要遍历,hGetAll是禁忌法术。

ZSet

前面谈到了,其实延时队列就是一种有序堆结构,就是需要加上一个时间权重,那么,有序集合不就是这样的么?

入队:ZADD KEY timestamp task ,我们将需要处理的任务,按其需要延迟处理时间作为 Score 加入到 ZSet 中。
出队:ZRANGEBYSCORE KEY -inf +inf limit 0 1 WITHSCORES。这样就能取出需要执行的任务了。执行完后删除:Zremrangebyscore KEY -inf +inf limit

时间轮

其实以上的算法,都有个小问题,同一时间线的任务先后问题,比如都是凌晨00执行的,怎么谁先谁后?因为任务是有优先级或者顺序的,当然也可以按优先级设置多个key,思路有很多。

这里在介绍一种算法,也是很多消息队列软件使用的,时间轮算法。

其实也很简单,就是每个时间点放一个队列,然后用一个任务去扫描时间轮,就像时钟一样,这样就能到点执行对应的任务了。

图片来源于网络

比如任务扫描到了2,需要添加一个延时3秒的任务,就直接添加到5上面。

当然对于时间粒度不同,我们肯定要设置多个时间轮,就像时针分针秒针。

这样的好处是什么?

  • 前面两种方式,说到底,需要遍历+排序,而时间轮,只需要逐步扫描逐步取出任务就好了。效率上高了很多,任务越多越明显。
原文地址:https://www.cnblogs.com/HappyTeemo/p/14589198.html