4種延時任務實現方案

業務場景

我們買火車票或者叫外賣的時候,下完單之後會跳轉到支付頁面,頁面裏通常會有一個計時器,要求在指定時間內完成支付,否則訂單就會被自動取消。這就是延時任務的一個典型業務場景。分析這個場景,其實最關鍵的就是如何在訂單超時的時候立即觸發取消訂單的動作。

那麼如何實現這種延時業務呢?通常有以下4種方案。

定時任務輪詢db

用戶下單後db中會生成一條訂單記錄,記錄了訂單號、用戶ID、創建時間、訂單詳情、訂單狀態等信息。假設超時時間是600秒,我們後臺起一個定時任務,每隔固定時間運行一次,每次掃描db中的超時訂單select * from order where createTime <= now()-600,然後取消查詢到的訂單。

這種方法實現簡單,但是有很多缺點。超時時間通常是秒級的,如果定時任務每秒運行一次,那麼就相當於每秒就要對訂單表做一次掃描,這是相當消耗db資源的操作,因此定時任務一般不會設置爲秒級;但是如果設置爲分鐘級,又會犧牲即時性,比如600秒超時,很有可能660秒的時候訂單才被取消。

DelayQueue

JDK的DelayQueue(延遲隊列)是無界阻塞隊列,只有在延遲期滿時才能從中獲取元素。每生成一個訂單,在把訂單記錄到db的同時,要把訂單id等信息投遞到延遲隊列中去,隊列會按照超時時間進行排序,最先超時的訂單排在隊列的頭部;起一個單獨的線程不斷地從隊列中摘取元素然後去做取消訂單的動作。

這種方法最大的缺點就是沒有將超時信息持久化,服務重啓之後延遲隊列的元素不會被恢復。

redis的zset

在redis中創建一個key是”delayOrders”的zset,每個member就是訂單ID,member的score就是該訂單的超時時間戳。我們每次從zset中取出score最小也就是最先超時的元素,判斷其是否超時,如果超時就將其從zset中刪除並取消訂單,如果未超時就繼續執行下一次循環。

RabbitMQ的TTL+DLX

RabbitMQ可設置消息過期時間(TTL),當消息過期後可以將該消息投遞到隊列上設置的死信交換器(DLX)上。然後投遞到死信隊列中,重新消費。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章