溫馨提示×

您好,登錄后才能下訂單哦!

密碼登錄×
登錄注冊(cè)×
其他方式登錄
點(diǎn)擊 登錄注冊(cè) 即表示同意《億速云用戶服務(wù)條款》

聊聊業(yè)務(wù)系統(tǒng)中投遞消息到mq的幾種方式

發(fā)布時(shí)間:2020-06-28 21:52:06 來源:網(wǎng)絡(luò) 閱讀:241 作者:路人甲Java 欄目:編程語(yǔ)言

背景

電商中有這樣的一個(gè)場(chǎng)景:

  1. 下單成功之后送積分的操作,我們使用mq來實(shí)現(xiàn)
  2. 下單成功之后,投遞一條消息到mq,積分系統(tǒng)消費(fèi)消息,給用戶增加積分

我們主要討論一下,下單及投遞消息到mq的操作,如何實(shí)現(xiàn)?每種方式優(yōu)缺點(diǎn)?

方式一

step1:start transaction
step2:生成訂單
step3:投遞消息到mq
step4:commit transaction

這種方式是將發(fā)送消息放在了事務(wù)提交之前,可能存在的問題:

step3發(fā)生異常

導(dǎo)致step4失敗,下單失敗,直接影響到下單業(yè)務(wù)

step4發(fā)生異常,其他step成功

下單失敗,消息投遞成功,給用戶增加了積分

方式二

我們將發(fā)送消息放到事務(wù)之后進(jìn)行:
step1:start transaction
step2:生成訂單
step3:commit transaction
step4:投遞消息到mq

可能會(huì)出現(xiàn)的問題:
step4發(fā)生異常,其他step成功

導(dǎo)致下單成功,投遞消息失敗,用戶未增加積分

上面兩種是比較常見的做法,也是最容易出錯(cuò)的。

方式三

step1:start transaction
step2:生成訂單
step3:本地庫(kù)中插入一條需要發(fā)送消息的記錄t_msg_record
step3:commit transaction
step5:新增一個(gè)定時(shí)器,輪詢t_msg_record,將待發(fā)送的記錄投遞到mq中

這種方式借助了數(shù)據(jù)庫(kù)的事務(wù),業(yè)務(wù)和消息記錄作為了一個(gè)原子操作,業(yè)務(wù)成功之后,消息日志必定是存在的。解決了前兩種方式遇到的問題。如果我們的業(yè)務(wù)系統(tǒng)比較單一,可以采用這種方式。

對(duì)于微服務(wù)化的情況,上面這種方式不是太好,每個(gè)服務(wù)都需要上面的操作;也不利于擴(kuò)展。

方式四

增加一個(gè)消息服務(wù)消息庫(kù),負(fù)責(zé)消息的落庫(kù)、將消息發(fā)送投遞到mq。

step1:start transaction
step2:生成訂單
step3:當(dāng)前事務(wù)庫(kù)插入一條日志:生成一個(gè)唯一的業(yè)務(wù)id(bus_id),將bus_id和訂單關(guān)聯(lián)起來保存到當(dāng)前事務(wù)所在的庫(kù)中
step4:調(diào)用消息服務(wù):攜帶bus_id,將消息先落地入庫(kù),此時(shí)消息的狀態(tài)為待發(fā)送狀態(tài),返回消息id(msg_id)
step5:commit transaction
step6:如果上面都成功,調(diào)用消息服務(wù),將消息投遞到mq中;如果上面有失敗的情況,則調(diào)用消息服務(wù)取消消息的發(fā)送

能想到上面這種方式,已經(jīng)算是有很大進(jìn)步了,我們繼續(xù)分析一下可能存在的問題:

  1. 系統(tǒng)中增加了一個(gè)消息服務(wù),下單操作依賴于該服務(wù),業(yè)務(wù)對(duì)改服務(wù)依賴性比較高,當(dāng)消息服務(wù)不可用時(shí),整個(gè)業(yè)務(wù)將不可用。
  2. 若step6失敗,消息將處于待發(fā)送狀態(tài),此時(shí)業(yè)務(wù)方需要提供一個(gè)會(huì)查接口(通過bus_id查詢),驗(yàn)證業(yè)務(wù)是否執(zhí)行成功;消息服務(wù)需新增一個(gè)定時(shí)任務(wù),對(duì)于狀態(tài)為待發(fā)送狀態(tài)的消息做補(bǔ)償處理,檢查一下業(yè)務(wù)是否處理成功;從而確定消息是投遞還是取消發(fā)送
  3. step4依賴于消息服務(wù),如果消息服務(wù)性能不佳,會(huì)導(dǎo)致當(dāng)前業(yè)務(wù)的事務(wù)提交時(shí)間延長(zhǎng),容易產(chǎn)生死鎖,并導(dǎo)致并發(fā)性能降低。我們通常是比較忌諱在事務(wù)中做遠(yuǎn)程調(diào)用處理的,遠(yuǎn)程調(diào)用的性能和時(shí)間往往不可控,會(huì)導(dǎo)致當(dāng)前事務(wù)變?yōu)橐粋€(gè)大事務(wù),從而引發(fā)其他故障。

方式五

在以上方式中,我們繼續(xù)改進(jìn),進(jìn)而出現(xiàn)了更好的一種方式:

step1:生成一個(gè)全局唯一業(yè)務(wù)消息id(bus_msg_id),調(diào)用消息服務(wù),攜帶bus_msg_id,將消息先落地入庫(kù),此時(shí)消息的狀態(tài)為待發(fā)送狀態(tài),返回消息id(msg_id)
step2:start transaction
step3:生成訂單
step4:當(dāng)前事務(wù)庫(kù)插入一條日志(將step3中的業(yè)務(wù)和bus_msg_id關(guān)聯(lián)起來)
step5:commit transaction
step6:分2中情況:如果上面都成功,調(diào)用消息服務(wù),將消息投遞到mq中;如果上面有失敗的情況,則調(diào)用消息服務(wù)取消消息的發(fā)送

方式五和方式四對(duì)比,比較好的一個(gè)地方:將調(diào)用消息服務(wù),消息落地操作,放在了事務(wù)之外進(jìn)行,這點(diǎn)小的改進(jìn)其實(shí)算是一個(gè)非常好的優(yōu)化。

總結(jié)

  1. 若我們的系統(tǒng)系統(tǒng)比較小比較單一簡(jiǎn)單,建議采用方式三
  2. 若我們的系統(tǒng)采用微服務(wù)的方式,建議使用方式五
  3. 你們的系統(tǒng)中如何發(fā)送消息的,大家可以留言,我們一起討論,一起進(jìn)步。

mq系列整個(gè)內(nèi)容

  1. 聊聊mq的使用場(chǎng)景
  2. 聊聊業(yè)務(wù)系統(tǒng)中投遞消息到mq的幾種方式
  3. 如何確保投遞消息一定成功?
  4. 聊聊消息消費(fèi)的幾種方式
  5. 如何確保消息至少消費(fèi)一次
  6. 如何保證消息消費(fèi)的冪等性

路人甲Java,只生產(chǎn)干貨,公眾號(hào):javacode2018,喜歡的關(guān)注一下。

向AI問一下細(xì)節(jié)

免責(zé)聲明:本站發(fā)布的內(nèi)容(圖片、視頻和文字)以原創(chuàng)、轉(zhuǎn)載和分享為主,文章觀點(diǎn)不代表本網(wǎng)站立場(chǎng),如果涉及侵權(quán)請(qǐng)聯(lián)系站長(zhǎng)郵箱:is@yisu.com進(jìn)行舉報(bào),并提供相關(guān)證據(jù),一經(jīng)查實(shí),將立刻刪除涉嫌侵權(quán)內(nèi)容。

AI