你真的了解延時隊列嗎(一)


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沒有真正的管理計划

核心概念:調度器、任務和觸發器。

三者關系:調度器負責調度各個任務,到了某個時刻或者過了一定時間,觸發器觸動了,特定任務便啟動執行。

  1. scheduler是一個計划調度器容器(總部),容器里面可以盛放眾多的JobDetail和trigger,當容器啟動后,里面的每個JobDetail都會根據trigger按部就班自動去執行。
  2. JobDetail是一個可執行的工作,它本身是有狀態的。
  3. Trigger代表什么時候去調。
  4. 當JobDetail和Trigger在scheduler容器上注冊后,形成了裝配好的作業(JobDetail和Trigger所組成的一對兒),就可以伴隨容器啟動而調度執行了。
  5. scheduler是個容器,容器中有一個線程池,用來並行調度執行每個作業,這樣可以提高容器效率。

 

 來源於:https://juejin.im/post/5b5a0bf9f265da0f6523913b

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM