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是個容器,容器中有一個執行緒池,用來並行排程執行每個作業,這樣可以提高容器效率。
待續。。。
如果上面問題有什麼疑問的話可以關注公眾號,來和我一起討論吧,關注即可免費領取海量最新java學習資料視訊,以及最新面試資料。
如果大家覺得這篇文章對你有幫助,或者你有什麼疑問想提供1v1免費vip服務,都可以關注我的公眾號,關注即可免費領取海量最新java學習資料視訊,以及最新面試資料,你的關注和轉發是對我最大的支援,O(∩_∩)O: