1 使用場景
- 關閉空閑連接。服務器中,有很多客戶端的連接,空閑一段時間之后需要關閉之。
- 清理過期數據業務上。比如緩存中的對象,超過了空閑時間,需要從緩存中移出。
- 任務超時處理。在網絡協議滑動窗口請求應答式交互時,處理超時未響應的請求。
- 下單之后如果三十分鍾之內沒有付款就自動取消訂單。
- 訂餐通知:下單成功后60s之后給用戶發送短信通知。
- 當訂單一直處於未支付狀態時,如何及時的關閉訂單,並退還庫存?
- 如何定期檢查處於退款狀態的訂單是否已經退款成功?
- 新創建店鋪,N天內沒有上傳商品,系統如何知道該信息,並發送激活短信?
- 定時任務調度:使用DelayQueue保存當天將會執行的任務和執行時間,一旦從DelayQueue中獲取到任務就開始執行。
2 延時任務-實現方式
- 定期輪詢(數據庫等)
- DelayQueue
- Timer
- ScheduledExecutorService
- 時間輪(kafka)
- RabbitMQ
- Quartz
- Redis Zset
- Koala
- JCronTab
- SchedulerX(阿里)
- 有贊延遲隊列
2.1 輪詢
特點:定期輪訓數據庫,設置狀態。
優點:實現簡單
缺點:數據量過大時會消耗太多的IO資源,效率太低
2.2 DelayQueue
特點: 無界、延遲、阻塞隊列
a、BlockingQueue+PriorityQueue(堆排序)+Delayed
b、DelayQueue中存放的對象需要實現compareTo()方法和getDelay()方法。
c、getDelay方法返回該元素距離失效還剩余的時間,當<=0時元素就失效了, 就可以從隊列中獲取到。
這里為什么要用leader/follower模式?
- 如果不是隊首節點,根本不需要喚醒操作!
- 假設取值時,延時時間還沒有到,那么需要等待,但這個時候,隊列中新加入了一個延時更短的,並放在了隊首,那么 此時,for循環由開始了,取得是新加入的元素,那之前的等待就白等了,明顯可以早點退出等待!
- 還有就是如果好多線程都在此等待,如果時間到了,同時好多線程會充等待隊列進入鎖池中,去競爭鎖資源,但結果只能是一個成功, 多了寫無畏的競爭!(多次的等待和喚醒)
2.3 Timer與TimerTask
- TaskQueue中的排序是對TimerTask中的下一次執行時間進行堆排序,每次去取數組第一個。
- 而delayQueue是對queue中的元素的getDelay()結果進行排序
Timer是一種定時器工具,用來在一個后台線程計划執行指定任務。它可以計划執行一個任務一次或反復多次。 主要方法:
![]()
2.4 時間輪(kafka)
時間輪名詞解釋:
- 時間格:環形結構中用於存放延遲任務的區塊;
- 指針(CurrentTime):指向當前操作的時間格,代表當前時間
- 格數(ticksPerWheel):為時間輪中時間格的個數
- 間隔(tickDuration):每個時間格之間的間隔
- 總間隔(interval):當前時間輪總間隔,也就是等於ticksPerWheel*tickDuration
根據每個TimerTaskEntry的過期時間和當前時間輪的時間,選擇一個合適的bucket(實際上就是TimerTaskList),把這個TimerTaskEntry對象放進去,同時如果bucket的過期時間有更新,就將這個bucket推進DelayQueue,重新排序
例子:假設編號為0的時間格或者桶保存着到期時間為t,每一個tick的持續時間(tickDuration)為20ms,在這個格子里只能保存着到期時間為[t~t+20]ms的任務,假設時間輪的時間格有n個,每一個間隔1ms,到期時間為m(ms),那么計算公式m%n = 所在的時間格或者桶,比如n=10,m=34ms,那么他所在桶或者時間格是4
2.5 RabbitMQ-延時任務
RabbitMQ本身沒有直接支持延遲隊列功能,但是可以通過以下特性模擬出延遲隊列的功能。
RabbitMQ可以針對Queue和Message設置 x-message-tt,來控制消息的生存時間,如果超時,則消息變為dead letter RabbitMQ針對隊列中的消息過期時間有兩種方法可以設置。 A: 通過隊列屬性設置,隊列中所有消息都有相同的過期時間。 B: 對消息進行單獨設置,每條消息TTL可以不同。
2.6 Quartz
為什么不用Timer?
- Timers沒有持久化機制.
- Timers不靈活 (只可以設置開始時間和重復間隔,不是基於時間、日期、天等(秒、分、時)的)
- Timers 不能利用線程池,一個timer一個線程
- Timers沒有真正的管理計划
核心概念:調度器、任務和觸發器。
三者關系:調度器負責調度各個任務,到了某個時刻或者過了一定時間,觸發器觸動了,特定任務便啟動執行。
- scheduler是一個計划調度器容器(總部),容器里面可以盛放眾多的JobDetail和trigger,當容器啟動后,里面的每個JobDetail都會根據trigger按部就班自動去執行。
- JobDetail是一個可執行的工作,它本身是有狀態的。
- Trigger代表什么時候去調。
- 當JobDetail和Trigger在scheduler容器上注冊后,形成了裝配好的作業(JobDetail和Trigger所組成的一對兒),就可以伴隨容器啟動而調度執行了。
- scheduler是個容器,容器中有一個線程池,用來並行調度執行每個作業,這樣可以提高容器效率。
來源於:https://juejin.im/post/5b5a0bf9f265da0f6523913b