每日一博 - 使用环形队列实现高效的延时消息
文章目錄
- Pre
- 方案A
- 方案B
- 總結
Pre
來個場景: 24小時后將未進行某個Action的業務,執行另外一個動作。 比如 24小時未付款的訂單,取消。
你可能會說
方案A
來個定時唄 ,每隔半小時 ,掃描數據庫訂單表,將完成時間超過24小時的訂單,取消掉。
But…
這方案有些明顯的缺點啊,老哥
-
(1)輪詢效率比較低
-
(2)時效性不好,假設每小時輪詢一次,最壞的情況下,時間誤差會達到1小時;
那如何既保證效率的同時,又保證實時性呢?
方案B
來說下核心思路
高效延時消息,包含兩個重要的數據結構:
-
環形隊列。例如可以創建一個大小為3600的環形隊列
-
任務集合。環上每一個格是一個Set
同時,啟動一個timer:
-
每隔1s,timer在環形隊列中移動一格
-
用一個Current Index來標識當前所在的格;
Task結構中包含兩個重要屬性:
-
Cycle-Num:用于標記當第幾圈掃描到這個格時,執行任務
-
Task-Function:到時間后需要執行的任務函數
假設當前Current Index指向第一格,當有延時消息到達之后,例如希望3620秒之后,觸發一個延時消息任務:
-
(1)計算這個Task應該放在哪一個格,現在是在第1格,3610秒之后,應該是第11格,所以這個Task應該加入第11格的Set<Task>中;
-
(2)計算這個Task的Cycle-Num,由于環形隊列是3600格(每秒移動一格,正好1小時),這個任務是3610秒后執行。所以應該繞3610/3600=1圈之后再執行,于是Cycle-Num=1;
Current Index每秒移動一格,當移動到下一格時,遍歷這個格的Set,看看每個Task的Cycle-Num是不是0:
-
如果不是0,說明任務時間還沒到,還需要多移動幾圈,將Cycle-Num減1;
-
如果是0,說明到這個Task的執行時間了,取出Task-Funciton丟給工作線程執行,并把這個Task從Set<Task>中刪除
總結
總體思路就是這個樣子,總結下有點
-
(1)效率高,無需再輪詢訂單表;一個訂單,任務只執行一次
-
(2)實時性好,精確到秒
總結
以上是生活随笔為你收集整理的每日一博 - 使用环形队列实现高效的延时消息的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 架构漫谈 - 数据治理核心思路及解决方案
- 下一篇: 每日一博 - ThreadLocal V