實現(xiàn)電商系統(tǒng)定時自動關(guān)閉訂單

一胧后、需求描述

在電商、支付等領(lǐng)域抱环,往往會有這樣的場景壳快,用戶下單后放棄支付了,那這筆訂單會在指定的時間段后進行關(guān)閉操作江醇,而且時間很準(zhǔn)確濒憋,誤差在1s內(nèi)。

二陶夜、實現(xiàn)方案

  • 定時任務(wù)關(guān)閉訂單(不推薦)
  • RocketMQ延遲隊列(不夠靈活)
  • RabbitMQ死信隊列(不推薦)
  • RabbitMQ的delay插件rabbitmq_delayed_message_exchange實現(xiàn)延時消息(推薦)
  • 時間輪算法
  • Redis過期監(jiān)聽

三、方案詳解

3.1 定時任務(wù)關(guān)閉訂單

一般情況下裆站,最不推薦的方式就是關(guān)單方式就是定時任務(wù)方式条辟。我們假設(shè),關(guān)單時間為下單后10分鐘宏胯,定時任務(wù)間隔也是10分鐘羽嫡,如果在第1分鐘下單,在第20分鐘的時候才能被掃描到執(zhí)行關(guān)單操作肩袍,這樣誤差達到10分鐘杭棵,這在很多場景下是不可接受的,另外需要頻繁掃描主訂單號造成網(wǎng)絡(luò)IO和磁盤IO的消耗,對實時交易造成一定的沖擊魂爪,所以PASS先舷。

3.2 RocketMQ延遲隊列方式

延遲消息 生產(chǎn)者把消息發(fā)送到消息服務(wù)器后,并不希望被立即消費滓侍,而是等待指定時間后才可以被消費者消費蒋川,這類消息通常被稱為延遲消息。 在RocketMQ開源版本中撩笆,支持延遲消息捺球,但是不支持任意時間精度的延遲消息,只支持特定級別的延遲消息夕冲。 消息延遲級別分別為1s 5s 10s 30s 1m 2m 3m 4m 5m 6m 7m 8m 9m 10m 20m 30m 1h 2h氮兵,共18個級別。

這種方式相比定時任務(wù)好了很多歹鱼,但是有一個致命的缺點泣栈,就是延遲等級只有18種(商業(yè)版本支持自定義時間),如果我們想把關(guān)閉訂單時間設(shè)置在15分鐘該如何處理呢醉冤?顯然不夠靈活秩霍。

3.3 RabbitMQ死信隊列

Rabbitmq本身是沒有延遲隊列的,只能通過Rabbitmq本身隊列的特性來實現(xiàn)蚁阳,想要Rabbitmq實現(xiàn)延遲隊列铃绒,需要使用Rabbitmq的死信交換機(Exchange)和消息的存活時間TTL(Time To Live)。

一個消息在滿足如下條件下螺捐,會進死信交換機颠悬,記住這里是交換機而不是隊列,一個交換機可以對應(yīng)很多隊列定血。

一個消息被Consumer拒收了赔癌,并且reject方法的參數(shù)里requeue是false。也就是說不會被再次放在隊列里澜沟,被其他消費者使用灾票。 上面的消息的TTL到了,消息過期了茫虽。

隊列的長度限制滿了刊苍,排在前面的消息會被丟棄或者扔到死信路由上。 死信交換機就是普通的交換機濒析,只是因為我們把過期的消息扔進去正什,所以叫死信交換機,并不是說死信交換機是某種特定的交換機号杏。

消息TTL(消息存活時間) 消息的TTL就是消息的存活時間婴氮。RabbitMQ可以對隊列和消息分別設(shè)置TTL。對隊列設(shè)置就是隊列沒有消費者連著的保留時間,也可以對每一個單獨的消息做單獨的設(shè)置主经。超過了這個時間荣暮,我們認(rèn)為這個消息就死了,稱之為死信旨怠。如果隊列設(shè)置了渠驼,消息也設(shè)置了,那么會取值較小的迷扇。所以一個消息如果被路由到不同的隊列中蜓席,這個消息死亡的時間有可能不一樣(不同的隊列設(shè)置)课锌。這里單講單個消息的TTL,因為它才是實現(xiàn)延遲任務(wù)的關(guān)鍵雏胃。

可以通過設(shè)置消息的expiration字段或者x-message-ttl屬性來設(shè)置時間志鞍,兩者是一樣的效果。只是expiration字段是字符串參數(shù)统翩,所以要寫個int類型的字符串此洲,當(dāng)上面的消息扔到隊列中后,過了60秒娶桦,如果沒有被消費趟紊,它就死了碰酝。不會被消費者消費到戴差。這個消息后面的,沒有“死掉”的消息對頂上來墨吓,被消費者消費纹磺。死信在隊列中并不會被刪除和釋放,它會被統(tǒng)計到隊列的消息數(shù)中去秘症。

3.4 RabbitMQ的delay插件rabbitmq_delayed_message_exchange實現(xiàn)延時消息
3.4.1 安裝插件

去RabbitMQ的官網(wǎng)下載插件乡摹,插件地址:https://www.rabbitmq.com/community-plugins.html采转,直接搜索rabbitmq_delayed_message_exchange即可找到我們需要下載的插件,下載和RabbitMQ配套的版本板熊。

3.4.2 與RabbitMQ死信隊列實現(xiàn)方式對比
  • 死信隊列:死信隊列是這樣一個隊列察绷,如果消息發(fā)送到該隊列并超過了設(shè)置的時間克婶,就會被轉(zhuǎn)發(fā)到設(shè)置好的處理超時消息的隊列當(dāng)中去,利用該特性可以實現(xiàn)延遲消息鸭蛙。
  • 延遲插件:通過安裝插件筋岛,自定義交換機睁宰,讓交換機擁有延遲發(fā)送消息的能力,從而實現(xiàn)延遲消息孝赫。
  • 總結(jié):由于死信隊列方式需要創(chuàng)建兩個交換機(死信隊列交換機+處理隊列交換機)红符、兩個隊列(死信隊列+處理隊列)伐债,而延遲插件方式只需創(chuàng)建一個交換機和一個隊列峰锁,所以后者使用起來更簡單双戳。
3.5 時間輪算法
1

1魄衅、創(chuàng)建環(huán)形隊列膏斤,例如可以創(chuàng)建一個包含3600個slot的環(huán)形隊列(本質(zhì)是個數(shù)組)

2、任務(wù)集合莫辨,環(huán)上每一個slot是一個Set,同時啟動一個timer盘榨,這個timer每隔1s蟆融,在上述環(huán)形隊列中移動一格型酥,有一個Current Index指針來標(biāo)識正在檢測的slot。

Task結(jié)構(gòu)中有兩個很重要的屬性:
1郁竟、Cycle-Num:當(dāng)Current Index第幾圈掃描到這個Slot時由境,執(zhí)行任務(wù)虏杰。
2、訂單號:要關(guān)閉的訂單號(也可以是其他信息瘸彤,比如:是一個基于某個訂單號的任務(wù))

假設(shè)當(dāng)前Current Index指向第0格笛钝,例如在3610秒之后,有一個訂單需要關(guān)閉拯杠,只需:
1潭陪、計算這個訂單應(yīng)該放在哪一個slot依溯,當(dāng)我們計算的時候現(xiàn)在指向1瘟则,3610秒之后醋拧,應(yīng)該是第10格丹壕,所以這個Task應(yīng)該放在第10個slot的Set中。
2缭乘、計算這個Task的Cycle-Num琉用,由于環(huán)形隊列是3600格(每秒移動一格,正好1小時)奴紧,這個任務(wù)是3610秒后執(zhí)行绰寞,所以應(yīng)該繞3610/3600=1圈之后再執(zhí)行铣口,于是Cycle-Num=1

Current Index不停的移動,每秒移動到一個新slot脑题,這個slot中對應(yīng)的Set叔遂,每個Task看Cycle-Num是不是0。如果不是0蚕苇,說明還需要多移動幾圈,將Cycle-Num減1涩笤。如果是0蹬碧,說明馬上要執(zhí)行這個關(guān)單Task了,取出訂單號執(zhí)行關(guān)單(可以用單獨的線程來執(zhí)行Task)炒刁,并把這個訂單信息從Set中刪除即可恩沽。
1、無需再輪詢?nèi)坑唵蜗枋迹矢?br> 2罗心、一個訂單,任務(wù)只執(zhí)行一次
3绽昏、時效性好协屡,精確到秒(控制timer移動頻率可以控制精度)。

3.6 Redis過期監(jiān)聽

1全谤、修改redis.windows.conf配置文件中notify-keyspace-events的值肤晓,默認(rèn)配置notify-keyspace-events的值為 "" 修改為 notify-keyspace-events Ex 這樣便開啟了過期事件

2、 創(chuàng)建配置類RedisListenerConfig(配置RedisMessageListenerContainer這個bean)认然。

3补憾、繼承KeyExpirationEventMessageListener創(chuàng)建Redis過期事件的監(jiān)聽類盈匾。

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末未巫,一起剝皮案震驚了整個濱河市劈伴,隨后出現(xiàn)的幾起案子严里,更是在濱河造成了極大的恐慌,老刑警劉巖教硫,帶你破解...
    沈念sama閱讀 216,372評論 6 498
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場離奇詭異,居然都是意外死亡盾碗,警方通過查閱死者的電腦和手機,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 92,368評論 3 392
  • 文/潘曉璐 我一進店門堰怨,熙熙樓的掌柜王于貴愁眉苦臉地迎上來灿巧,“玉大人绞吁,你說我怎么就攤上這事颜说『盎” “怎么了?”我有些...
    開封第一講書人閱讀 162,415評論 0 353
  • 文/不壞的土叔 我叫張陵,是天一觀的道長。 經(jīng)常有香客問我爸舒,道長苛聘,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 58,157評論 1 292
  • 正文 為了忘掉前任,我火速辦了婚禮,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘重贺。我一直安慰自己,他們只是感情好缸棵,可當(dāng)我...
    茶點故事閱讀 67,171評論 6 388
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著狰贯,像睡著了一般幔摸。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 51,125評論 1 297
  • 那天宇挫,我揣著相機與錄音苛吱,去河邊找鬼。 笑死器瘪,一個胖子當(dāng)著我的面吹牛翠储,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播橡疼,決...
    沈念sama閱讀 40,028評論 3 417
  • 文/蒼蘭香墨 我猛地睜開眼欣除,長吁一口氣:“原來是場噩夢啊……” “哼澈蟆!你這毒婦竟也來了?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 38,887評論 0 274
  • 序言:老撾萬榮一對情侶失蹤灰追,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后刽肠,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體溃肪,經(jīng)...
    沈念sama閱讀 45,310評論 1 310
  • 正文 獨居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 37,533評論 2 332
  • 正文 我和宋清朗相戀三年音五,在試婚紗的時候發(fā)現(xiàn)自己被綠了惫撰。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點故事閱讀 39,690評論 1 348
  • 序言:一個原本活蹦亂跳的男人離奇死亡躺涝,死狀恐怖厨钻,靈堂內(nèi)的尸體忽然破棺而出扼雏,到底是詐尸還是另有隱情,我是刑警寧澤夯膀,帶...
    沈念sama閱讀 35,411評論 5 343
  • 正文 年R本政府宣布诗充,位于F島的核電站,受9級特大地震影響诱建,放射性物質(zhì)發(fā)生泄漏蝴蜓。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點故事閱讀 41,004評論 3 325
  • 文/蒙蒙 一俺猿、第九天 我趴在偏房一處隱蔽的房頂上張望茎匠。 院中可真熱鬧,春花似錦押袍、人聲如沸诵冒。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,659評論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽汽馋。三九已至,卻和暖如春圈盔,著一層夾襖步出監(jiān)牢的瞬間豹芯,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 32,812評論 1 268
  • 我被黑心中介騙來泰國打工驱敲, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留告组,地道東北人。 一個月前我還...
    沈念sama閱讀 47,693評論 2 368
  • 正文 我出身青樓癌佩,卻偏偏與公主長得像,于是被迫代替她去往敵國和親便锨。 傳聞我的和親對象是個殘疾皇子围辙,可洞房花燭夜當(dāng)晚...
    茶點故事閱讀 44,577評論 2 353

推薦閱讀更多精彩內(nèi)容