前言
消息發送一致性問題:
在分佈式部署環境下,通過網絡進行通訊,就會有數據傳世的不確定性,也就是CAP中的P【會出現分區容錯性的問題】。主動方發送消息到消息中間件以及消息中間件到被動方應用題都會出現網絡的問題;
如何保證一致性問題
場景:比如說支付模塊,訂單完成之後需要通知其他系統的場景。
解決方式1:先處理本地事務;再發送消息
問題:如果本地事務操作成功,但是由於各種異常情況(本身應用故障|消息系統故障|網絡故障),消息發不出去,就造成了訂單系統與其他三方系統的數據不一致的情況。
解決方式2:先發送消息,再處理本地事務。
問題:這種情況,更糟糕了,消息發出去了,但是本地事務操作失敗,也會造成了訂單系統與其他三方系統的數據不一致的情況。
解決方式3:
JMS標準中的XA協議是否可以保證發送一致性呢?
JMS的標準API中是有支持XA協議的全局性事務型接口。
問題:引入XA方式的分佈式事務,要求所有操作的資源必須支持XA協議,兩階段提交協議本身的成本;持久化成本高,全局鎖定,性能低。
解決方式4:
1.主動方先把消息發送給消息中間件,消息狀態爲待確認。
2.消息中間件收到消息後,只做存儲,不會投遞;
3.消息中間件返回持久化結果(成功/失敗)。
如果失敗,則結束操作;如果成功,接着執行業務操作。
4.業務操作完成後,需要吧操作結果發送給消息中間件。
5.消息中間件收到業務操作結果後,根據結果進行處理。 如果成功,則把消息狀態改爲“待發送”;後續進行投遞; 如果失敗,則刪除消息,結束。
5.前面流程ok之後, 就會進行消息投遞。
異常情況分析
從發送方的角度來看
異常情況 | 場景分析(原因分析) | 是否一致 |
---|---|---|
預發送失敗 | 消息未持久化;業務操作爲存儲;可能是因爲(發送方應用異常,網絡,中間件,消息存儲等異常) | 一致 |
預發送消息成功:主動方未收到消息存儲結果;消息未持久化;業務未執行 | 消息未持久化;業務未執行 | 一致 |
預發送消息成功:主動方未收到消息存儲結果;消息已持久化 ;業務未執行 | 消息已持久化;業務未執行 | 不一致 |
收到消息持久化成功的消息:還沒來得及執行業務操作就異常,或者執行失敗了 | 消息已持久化;業務未執行 | 不一致 |
從中間件的角度來看
異常情況 | 場景分析(原因分析) | 是否一致 |
---|---|---|
中間件未收到主動方的操作結果1 | 消息已持久化;業務操作未執行(或者失敗回滾) | 不一致 |
中間件未收到主動方的操作結果2 | 消息已持久化;業務操作成功 | 不一致 |
消息中間件收到操作結果,但持久化過程中失敗1 | 消息持久化(待確認,狀態應該更新爲廢棄);業務操作未執行(或者失敗回滾) | 不一致 |
消息中間件收到操作結果,但持久化過程中失敗2 | 消息持久化(待確認,狀態應該更新爲待發送);業務操作成功 | 不一致 |
總結髮送端的異常場景:
異常情況 | 是否一致 | 處理方案 |
---|---|---|
消息未持久化,業務操作未執行 | 一致 | 一致 |
消息已持久化(狀態待確認),業務操作未執行 | 不一致 | 查詢確認業務操作結果,刪除消息 |
消息已持久化(狀態待確認),業務已經執行 | 不一致 | 查詢確認業務操作結果,更新爲待發送狀態,執行投遞 |
MQ隊列消息
模型的特點:
1.消息生產者將消息發送到Queue中,消費者監聽queue,並接受消息。
2.消息被確認消費之後,會從queue中刪除;
3.queue支持多個消費者消費,但對某一個消息,只會有一個消費者消費成功
生成與消費的流程:
1.生產者生成併發送給MQ(同步或者異步)
2.MQ接收到消息,持久化到消息存儲(持久化是可選 )
3.MQ向producer返回消息的接收結果
4.消費者會監聽並消費MQ的消息
5.消費者獲取消息後,執行業務,對消費成功的消息向MQ發送ACk確認
6.確認後,會從隊列中刪除。
結論:正常的MQ隊列的處理流程無法真正實現消息發送一致性;因爲他進入隊列中沒有等到業務確認(或者生產者沒有收到消息的確認收到的消息)就進行投遞了。會存在一定的風險。
從消費者的角度來看
異常情況 | 場景分析(原因分析) | 操作 |
---|---|---|
1 | 消費端收到消息,業務處理後應用(或者網絡異常或者超時)異常,消息中間件不知道處理結果 | 重新投遞 |
2 | 消費端收到消息,業務處理完成,消息中間件的問題導致收不到ACK應答 | 重新投遞 |
3 | 消費端收到消息,業務處理完成,消息中間件收到應答,但持久化異常 | 重新投遞 |
總體來說,消費過程中肯定會出現重複投遞的問題,基本上就是因爲沒有及時確認ACK,所以消費端一定要在業務層面做好冪等的控制。但是消息重發也是有次數限制,會有down機的風險的,所以會限制次數,人工介入干預;後期定期清理。
總結
基於以上的常規以及異常的流程,我們就可以設計一套一致性的系統。 但目前現有的MQ中間件不支持消息發送的一致性流程(先持久化再被業務確認後然後再發送); 如果直接改造源碼,難度比較大,並且後續人員維護也非常繁瑣。
至於變通的方案,下一篇會講到 :分佈式事務實戰(三)–可靠性消息的最終一致性方案【基於本地消息服務】