天天看點

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,思路有很多。

這裡在介紹一種算法,也是很多消息隊列軟體使用的,時間輪算法。

其實也很簡單,就是每個時間點放一個隊列,然後用一個任務去掃描時間輪,就像時鐘一樣,這樣就能到點執行對應的任務了。

PHP怎樣寫延時隊列(定時器)

比如任務掃描到了2,需要添加一個延時3秒的任務,就直接添加到5上面。

當然對于時間粒度不同,我們肯定要設定多個時間輪,就像時針分針秒針。

PHP怎樣寫延時隊列(定時器)

這樣的好處是什麼?

  • 前面兩種方式,說到底,需要周遊+排序,而時間輪,隻需要逐漸掃描逐漸取出任務就好了。效率上高了很多,任務越多越明顯。
php