rocketmq能解決分布式事務嗎?之前提到發送消息的時候,可能存在消息的丢失,也就是說可能消息根本就沒有進入到MQ就丢了,然後沒有解釋過多的東西就直接切入了RocketMQ事務消息的方案,其實通過RocketMQ事務消息機制的研究,現在可以确信一點,如果使用事務消息機制去發送消息到MQ,一定是可以保證消息必然發送到MQ的,不會丢,我來為大家科普一下關于rocketmq能解決分布式事務嗎?下面希望有你要的答案,我們一起來看看吧!
之前提到發送消息的時候,可能存在消息的丢失,也就是說可能消息根本就沒有進入到MQ就丢了,然後沒有解釋過多的東西就直接切入了RocketMQ事務消息的方案,其實通過RocketMQ事務消息機制的研究,現在可以确信一點,如果使用事務消息機制去發送消息到MQ,一定是可以保證消息必然發送到MQ的,不會丢。
但是這個事務消息機制其實挺複雜的,先得發送half消息,然後還得發送rollback/commit的請求,要是中間有點什麼問題,MQ還得回調你的接口。
我們真的有必要使用這麼複雜的機制去确保消息到達MQ,而且絕對不會丢嗎?畢竟這麼複雜的機制完全有可能導緻整體性能比較差,而且吞吐量比較低,是否有更加簡單的方法來确保消息一定可以到達MQ呢?
能不能基于重試機制來确保消息到達MQ?
想到這裡,可能内心已經有一個想法了,就是我們之前覺得發消息到MQ,無非就是覺得可能半路上消息給丢失了,然後消息根本沒有進入到MQ中,我們也沒做什麼額外的措施,就導緻消息找不回來了。
那麼我們先搞清楚一個問題,我們發送消息到MQ,然後我們可以等待MQ返回響應給我們,在什麼樣的情況下,MQ會返回響應給我們呢?
答案是顯而易見的,就是MQ收到消息之後寫入本地磁盤文件了,當然這個時候可能僅僅是寫入os cache中,但是隻要他寫入自己本地存儲了,就會返回響應給我們。
那麼隻要我們在代碼中發送消息到MQ之後,同步等待MQ返回響應給我們,一直等待,如果半路有網絡異常或者MQ内部異常,我們肯定會收到一個異常,比如網絡錯誤,或者請求超時之類的。
如果我們再收到異常之後,就認為消息發送MQ失敗了,然後再次重試嘗試發送消息到MQ,接着再次同步等待MQ返回響應給我們,這樣反複重試,是否可以确保消息一定會到達MQ?
理論上似乎存在一些短暫網絡異常的場景下,我們是可以通過不停的重試去保證消息到達MQ的,因為如果短時間網絡異常了,消息一直沒法發送,我們隻要不停的重試,網絡一旦恢複了,消息就可以發送到MQ了。
如果要是反複重試多次發現一直沒有辦法把消息投遞到MQ,此時我們就可以直接讓訂單系統回滾之前的流程,判定本次訂單支付交易失敗了。
看起來這個簡單的同步發送消息 反複重試的方案,也可以做到保證消息一定可以投遞到MQ中。
但是如果是在比較複雜的訂單業務場景中,僅僅采用同步發消息 反複重試多次的方案去确保消息絕對投遞到MQ中,似乎還是不夠的。
先執行訂單本地事務,還是先發消息到MQ?
如果我們先執行本地事務,接着再發送消息到MQ,看起來僞代碼可能是這樣的:
try {
// 執行訂單本地事務
orderService.finishOrderPay();
// 發送消息到MQ去
producer.sendMessage();
} catch (Exception e) {
// 如果發送消息失敗了,進行重試
for(int i=0;i < 3; i ) {
// 重試發送消息
}
// 如果多次重試發送消息之後,還是不行
// 回滾本地訂單事務
orderService.rollbackOrderPay();
}
上面這段代碼看起來似乎天衣無縫,先執行訂單本地事務,接着發送消息到MQ,如果本地事務執行失敗了,則不會繼續發送消息到MQ了。
如果訂單事務執行成功了,發送MQ失敗了,自動進行幾次重試,重試如果一直失敗,就回滾訂單。
但是這裡有一個問題,假設你剛執行完訂單本地事務,結果還沒等到你發送消息到MQ,結果你的訂單系統突然崩潰了,這就導緻你的訂單狀态可能已經修改為了"已完成",但是消息卻沒有發送到MQ中去,這就是這個方案最大的隐患。
如果出現這種場景,那你的多次重試發送MQ之類的代碼根本沒有機會執行,而且訂單本地事務還已經執行成功了,你的消息還沒發送出去,消費者肯定沒有辦法消費。
把訂單本地事務和重試發送MQ消息放到一個事務代碼中
接着考慮下一個問題,這時候可能有一個新的想法,如果把訂單本地事務代碼和發送MQ消息的代碼放到一個事務代碼中呢?
@Transactional
public void payOrderSuccess() {
try {
// 執行訂單本地事務
orderService.finishOrderPay();
// 發送消息到MQ去
producer.sendMessage();
} catch (Exception e) {
// 如果發送消息失敗了,進行重試
for(int i=0;i < 3; i ) {
// 重試發送消息
}
// 如果多次重試發送消息之後,還是不行
// 抛出異常,回滾本地事務
throw new XXXException();
}
}
上面這個代碼看起來似乎解決了我們的問題,就是在這個方法上加入事務,在這個事務方法中,我們哪怕執行了orderService.finishOrderPay(),但是其實也僅僅執行了一些增删改查的SQL語句,還沒提交訂單本地事務。
如果發送消息失敗了,而且多次重試還不奏效,則抛出異常會自動回滾本地事務。
如果你剛執行了 orderServicec.finishOrderPay(),結果訂單系統直接崩潰了,此時訂單本地事務會回滾,因為根本沒提交過。
但是對于這個方案,還是非常的不理想,原因就出在那個MQ多次重試的地方,假設用戶支付成功了,然後支付系統回調通知你的訂單系統說有一筆訂單支付成功了,這個時候你的訂單系統卡在多次重試MQ的代碼那裡,可能耗時了好幾秒中,此時回調通知及的系統早就等不及可能都超時異常了。
而且把重試MQ的代碼放在這個邏輯裡,可能導緻訂單系統的這個接口性能很差。
保證業務系統一緻性的最佳方案:基于RocketMQ的事務消息機制
綜合來看,真正要保證消息一定投遞到MQ,同時保證業務系統之間數據完全一緻,業内最佳的方案還是基于RocketMQ的事務消息機制。
,更多精彩资讯请关注tft每日頭條,我们将持续为您更新最新资讯!