背景 電商中有這樣的一個場景: 1. 下單成功之後送積分的操作,我們使用mq來實現 2. 下單成功之後,投遞一條消息到mq,積分系統消費消息,給用戶增加積分 我們主要討論一下,下單及投遞消息到mq的操作,如何實現?每種方式優缺點? 方式一 step1:start transaction step2: ...
背景
電商中有這樣的一個場景:
- 下單成功之後送積分的操作,我們使用mq來實現
- 下單成功之後,投遞一條消息到mq,積分系統消費消息,給用戶增加積分
我們主要討論一下,下單及投遞消息到mq的操作,如何實現?每種方式優缺點?
方式一
step1:start transaction
step2:生成訂單
step3:投遞消息到mq
step4:commit transaction
這種方式是將發送消息放在了事務提交之前,可能存在的問題:
step3發生異常
導致step4失敗,下單失敗,直接影響到下單業務
step4發生異常,其他step成功
下單失敗,消息投遞成功,給用戶增加了積分
方式二
我們將發送消息放到事務之後進行:
step1:start transaction
step2:生成訂單
step3:commit transaction
step4:投遞消息到mq
可能會出現的問題:
step4發生異常,其他step成功
導致下單成功,投遞消息失敗,用戶未增加積分
上面兩種是比較常見的做法,也是最容易出錯的。
方式三
step1:start transaction
step2:生成訂單
step3:本地庫中插入一條需要發送消息的記錄t_msg_record
step3:commit transaction
step5:新增一個定時器,輪詢t_msg_record,將待發送的記錄投遞到mq中
這種方式藉助了資料庫的事務,業務和消息記錄作為了一個原子操作,業務成功之後,消息日誌必定是存在的。解決了前兩種方式遇到的問題。如果我們的業務系統比較單一,可以採用這種方式。
對於微服務化的情況,上面這種方式不是太好,每個服務都需要上面的操作;也不利於擴展。
方式四
增加一個消息服務及消息庫,負責消息的落庫、將消息發送投遞到mq。
step1:start transaction
step2:生成訂單
step3:當前事務庫插入一條日誌:生成一個唯一的業務id(bus_id),將bus_id和訂單關聯起來保存到當前事務所在的庫中
step4:調用消息服務:攜帶bus_id,將消息先落地入庫,此時消息的狀態為待發送狀態,返回消息id(msg_id)
step5:commit transaction
step6:如果上面都成功,調用消息服務,將消息投遞到mq中;如果上面有失敗的情況,則調用消息服務取消消息的發送
能想到上面這種方式,已經算是有很大進步了,我們繼續分析一下可能存在的問題:
- 系統中增加了一個消息服務,下單操作依賴於該服務,業務對改服務依賴性比較高,當消息服務不可用時,整個業務將不可用。
- 若step6失敗,消息將處於待發送狀態,此時業務方需要提供一個會查介面(通過bus_id查詢),驗證業務是否執行成功;消息服務需新增一個定時任務,對於狀態為待發送狀態的消息做補償處理,檢查一下業務是否處理成功;從而確定消息是投遞還是取消發送
- step4依賴於消息服務,如果消息服務性能不佳,會導致當前業務的事務提交時間延長,容易產生死鎖,並導致併發性能降低。我們通常是比較忌諱在事務中做遠程調用處理的,遠程調用的性能和時間往往不可控,會導致當前事務變為一個大事務,從而引發其他故障。
方式五
在以上方式中,我們繼續改進,進而出現了更好的一種方式:
step1:生成一個全局唯一業務消息id(bus_msg_id),調用消息服務,攜帶bus_msg_id,將消息先落地入庫,此時消息的狀態為待發送狀態,返回消息id(msg_id)
step2:start transaction
step3:生成訂單
step4:當前事務庫插入一條日誌(將step3中的業務和bus_msg_id關聯起來)
step5:commit transaction
step6:分2中情況:如果上面都成功,調用消息服務,將消息投遞到mq中;如果上面有失敗的情況,則調用消息服務取消消息的發送
方式五和方式四對比,比較好的一個地方:將調用消息服務,消息落地操作,放在了事務之外進行,這點小的改進其實算是一個非常好的優化。
總結
- 若我們的系統系統比較小比較單一簡單,建議採用方式三
- 若我們的系統採用微服務的方式,建議使用方式五
- 你們的系統中如何發送消息的,大家可以留言,我們一起討論,一起進步。
mq系列整個內容
- 聊聊mq的使用場景
- 聊聊業務系統中投遞消息到mq的幾種方式
- 如何確保投遞消息一定成功?
- 聊聊消息消費的幾種方式
- 如何確保消息至少消費一次
- 如何保證消息消費的冪等性
路人甲Java,只生產乾貨,公眾號:javacode2018,喜歡的關註一下。