定时器的实现原理
文章目錄
- 1.定時器的作用?
- 2.數(shù)據(jù)結(jié)構(gòu)要求
- 3.時間輪
- 4.分級時間輪
- 5.業(yè)界實現(xiàn)方案
- 參考文獻
1.定時器的作用?
定時器的主要用途是執(zhí)行定時任務(wù)。
定時任務(wù)在很多場景都需要用到,比如游戲的 Buff 實現(xiàn),Redis 中的過期任務(wù),Linux 中的定時任務(wù),電商未支付訂單的關(guān)閉等等。
2.數(shù)據(jù)結(jié)構(gòu)要求
定時器需要支持如下幾個操作:
- 創(chuàng)建定時器
- 添加定時任務(wù)
- 取消定時任務(wù)
- 執(zhí)行到期任務(wù)(查找)
以下為常見實現(xiàn)定時器數(shù)據(jù)結(jié)構(gòu)的時間復(fù)雜度:
- 有序鏈表:插入O(n),刪除 O(1),過期 expire 執(zhí)行 O(1)
- 最小堆:插入O(logn),刪除 O(logn),過期 expire 執(zhí)行 O(1)
- 紅黑樹:插入O(logn),刪除 O(logn),過期 expire 執(zhí)行 O(logn)
- 哈希表+鏈表(時間輪):插入 O(1),刪除 O(1),過期 expire 平均執(zhí)行 O(1)(最壞為O(n))
不同開源框架定時器實現(xiàn)方式不一,如 libuv 采用最小堆,nginx 采用紅黑樹,linux 內(nèi)核和 skynet 采用時間輪算法等等。
3.時間輪
一個時間輪是一個環(huán)形結(jié)構(gòu),可以想象成時鐘,分為很多格子,一個格子代表一段時間(越短 Timer 精度越高),并用一個 List 保存在該格子上到期要執(zhí)行的所有任務(wù)。同時一個指針隨著時間流逝一格一格移動,并執(zhí)行對應(yīng) List 中所有到期的任務(wù)。任務(wù)通過取模決定應(yīng)該放入哪個格子。
以上圖為例,假設(shè)一個格子是1秒,則整個 wheel 能表示的時間段為 8s。
假如當前指針指向 0。
此時需要調(diào)度一個 3s 后執(zhí)行的任務(wù),顯然應(yīng)該加入到(0+3=3)的方格中,指針再走3次就可以執(zhí)行了。
如果任務(wù)要在10s后執(zhí)行,應(yīng)該等指針走完一個 round 零 2 格再執(zhí)行,因此應(yīng)放入2并將 round 標記為 2 表示第二輪時執(zhí)行。
4.分級時間輪
如果任務(wù)的時間跨度很大,數(shù)量也多,傳統(tǒng)的時間輪會造成任務(wù)的 round 很大,單個格子的任務(wù) List 很長,并會維持很長一段時間。
這時可將 Wheel 按時間粒度分級:
這種方式的優(yōu)點在于能夠保證任務(wù)鏈表的長度一直在比較短的狀態(tài),但缺點是需要更多的空間。
5.業(yè)界實現(xiàn)方案
業(yè)界對于定時器/延遲隊列的工程實踐,則通常使用以下幾種方案。
- 基于 Redis ZSet 實現(xiàn)。
- 采用某些自帶延遲選項的隊列實現(xiàn),如 RabbitMQ、Beanstalkd、騰訊 TDMQ 等。
- 基于 Timing-Wheel 時間輪算法實現(xiàn)。
參考文獻
如何快速實現(xiàn)一個定時器?- InfoQ
總結(jié)