延時(shí)任務(wù)實(shí)現(xiàn)方案
業(yè)務(wù)場(chǎng)景
我們買(mǎi)火車(chē)票或者叫外賣(mài)的時(shí)候,下完單之后會(huì)跳轉(zhuǎn)到支付頁(yè)面,頁(yè)面里通常會(huì)有一個(gè)計(jì)時(shí)器,要求在指定時(shí)間內(nèi)完成支付,否則訂單就會(huì)被自動(dòng)取消。這就是延時(shí)任務(wù)的一個(gè)典型業(yè)務(wù)場(chǎng)景。分析這個(gè)場(chǎng)景,其實(shí)最關(guān)鍵的就是如何在訂單超時(shí)的時(shí)候立即觸發(fā)取消訂單的動(dòng)作。
那么如何實(shí)現(xiàn)這種延時(shí)業(yè)務(wù)呢?通常有以下4種方案。
定時(shí)任務(wù)輪詢(xún)db
用戶下單后db中會(huì)生成一條訂單記錄,記錄了訂單號(hào)、用戶ID、創(chuàng)建時(shí)間、訂單詳情、訂單狀態(tài)等信息。假設(shè)超時(shí)時(shí)間是600秒,我們后臺(tái)起一個(gè)定時(shí)任務(wù),每隔固定時(shí)間運(yùn)行一次,每次掃描db中的超時(shí)訂單select * from order where createTime <= now()-600,然后取消查詢(xún)到的訂單。
這種方法實(shí)現(xiàn)簡(jiǎn)單,但是有很多缺點(diǎn)。超時(shí)時(shí)間通常是秒級(jí)的,如果定時(shí)任務(wù)每秒運(yùn)行一次,那么就相當(dāng)于每秒就要對(duì)訂單表做一次掃描,這是相當(dāng)消耗db資源的操作,因此定時(shí)任務(wù)一般不會(huì)設(shè)置為秒級(jí);但是如果設(shè)置為分鐘級(jí),又會(huì)犧牲即時(shí)性,比如600秒超時(shí),很有可能660秒的時(shí)候訂單才被取消。
DelayQueue
JDK的DelayQueue(延遲隊(duì)列)是無(wú)界阻塞隊(duì)列,只有在延遲期滿時(shí)才能從中獲取元素。每生成一個(gè)訂單,在把訂單記錄到db的同時(shí),要把訂單id等信息投遞到延遲隊(duì)列中去,隊(duì)列會(huì)按照超時(shí)時(shí)間進(jìn)行排序,最先超時(shí)的訂單排在隊(duì)列的頭部;起一個(gè)單獨(dú)的線程不斷地從隊(duì)列中摘取元素然后去做取消訂單的動(dòng)作。
這種方法最大的缺點(diǎn)就是沒(méi)有將超時(shí)信息持久化,服務(wù)重啟之后延遲隊(duì)列的元素不會(huì)被恢復(fù)。
redis的zset
在redis中創(chuàng)建一個(gè)key是”delayOrders”的zset,每個(gè)member就是訂單ID,member的score就是該訂單的超時(shí)時(shí)間戳。我們每次從zset中取出score最小也就是最先超時(shí)的元素,判斷其是否超時(shí),如果超時(shí)就將其從zset中刪除并取消訂單,如果未超時(shí)就繼續(xù)執(zhí)行下一次循環(huán)。
RabbitMQ的TTL+DLX
RabbitMQ可設(shè)置消息過(guò)期時(shí)間(TTL),當(dāng)消息過(guò)期后可以將該消息投遞到隊(duì)列上設(shè)置的死信交換器(DLX)上。然后投遞到死信隊(duì)列中,重新消費(fèi)。

四種方案對(duì)比
| 方案 | 優(yōu)點(diǎn) | 缺點(diǎn) |
|---|---|---|
| 定時(shí)任務(wù)輪詢(xún)db | 實(shí)現(xiàn)簡(jiǎn)單、無(wú)技術(shù)難點(diǎn)、異?;謴?fù)、支持分布式/集群環(huán)境 | 影響數(shù)據(jù)庫(kù)性能、時(shí)效性差 |
| DelayQueue | 實(shí)現(xiàn)簡(jiǎn)單、性能較好 | 無(wú)法異常恢復(fù)、分布式/集群實(shí)現(xiàn)困難 |
| redis的zset | 解耦、異?;謴?fù)、擴(kuò)展性強(qiáng)、支持分布式/集群環(huán)境 | 增加redis維護(hù)、占用帶寬 |
| RabbitMQ的TTL+DLX | 解耦、異常恢復(fù)、擴(kuò)展性強(qiáng)、支持分布式/集群環(huán)境 | 增加RabbitMQ維護(hù)、占用帶寬 |
source: //xiangxianzui.github.io/2020/02/延時(shí)任務(wù)實(shí)現(xiàn)方案

喜歡,在看
