消息順序、消息冪等、消息重復、消息事務、集群
https://www.cnblogs.com/xishuai/p/9174719.html
- 消息順序
場景:比如下單操作,下單成功之后,會發布創建訂單和扣減庫存消息,但扣減庫存消息執行會先於創建訂單消息,也就說前者執行成功之后,才能執行后者。
不保證完全按照順序消費,在 MQ 層面支持消息的順序處理開銷太大,為了極少量的需求,增加整體上的復雜度得不償失。
所以,還是在應用層面處理比較好,或者業務邏輯進行處理。
應用層解決方式:
- 消息實體中增加:版本號 & 狀態機 & msgid & parent_msgid,通過 parent_msgid 判斷消息的順序(需要全局存儲,記錄消息的執行狀態)。
- “同步執行”:當一個消息執行完之后,再發布下一個消息。
- 消息冪等、消息重復、消息事務
消息重復
造成消息重復的根本原因是:網絡不可達。只要通過網絡交換數據,就無法避免這個問題。所以解決這個問題的辦法就是繞過這個問題。那么問題就變成了:如果消費端收到兩條一樣的消息,應該怎樣處理?
消費端處理消息的業務邏輯保持冪等性。
保證每條消息都有唯一編號且保證消息處理成功與去重表的日志同時出現。
第 1 條很好理解,只要保持冪等性,不管來多少條重復消息,最后處理的結果都一樣。第 2 條原理就是利用一張日志表來記錄已經處理成功的消息的 ID,如果新到的消息 ID 已經在日志表中,那么就不再處理這條消息。
第 1 條解決方案,很明顯應該在消費端實現,不屬於消息系統要實現的功能。第 2 條可以消息系統實現,也可以業務端實現。正常情況下出現重復消息的概率其實很小,如果由消息系統來實現的話,肯定會對消息系統的吞吐量和高可用有影響,所以最好還是由業務端自己處理消息重復的問題,這也是 RabbitMQ 不解決消息重復的問題的原因。
RabbitMQ 不保證消息不重復,如果你的業務需要保證嚴格的不重復消息,需要你自己在業務端去重。
AMQP 消費者確認機制
AMQP 定義了消費者確認機制(message ack),如果一個消費者應用崩潰掉(此時連接會斷掉,broker 會得知),但是 broker 尚未獲得 ack,那么消息會被重新放入隊列。所以 AMQP 提供的是“至少一次交付”(at-least-once delivery),異常情況下,消息會被重復消費,此時業務要實現冪等性(重復消息處理)。
AMQP 生產者事務
對於生產者,AMQP 定義了事務(tx transaction)來確保生產消息被 broker 接收並成功入隊。TX 事務是阻塞調用,生產者需等待broker寫磁盤后返回的確認,之后才能繼續發送消息。事務提交失敗時(如broker宕機場景),broker並不保證提交的消息全部入隊。
TX 的阻塞調用使 broker 的性能非常差,RabbitMQ 使用 confirm 機制來優化生產消息的確認。Confirm 模式下,生產者可以持續發送消息,broker 將消息批量寫磁盤后回復確認,生產者通過確認消息的ID來確定哪些已發送消息被成功接收。Confirm 模式下生產者發送消息和接受確認是異步流程,生產者需要緩存未確認的消息以便出錯時重新發送。
總結
-
消息重復發布:不存在,因為 AMQP 定義了事務(tx transaction)來確保生產消息被 broker 接收並成功入隊。TX 事務是阻塞調用,生產者需等待 broker 寫磁盤后返回的確認,之后才能繼續發送消息。事務提交失敗時(如 broker 宕機場景),broker 並不保證提交的消息全部入隊。RabbitMQ 使用 confirm 機制來優化生產消息的確認(可以持續發布消息,但會批量回復確認)。
-
消息重復消費:AMQP 提供的是“至少一次交付”(at-least-once delivery),異常情況下,消息會被重復消費,此時業務要實現冪等性(重復消息處理)。
應用層解決方式: -
專門的 Map 存儲:用來存儲每個消息的執行狀態(用 msgid 區分),執行成功之后更新 Map,有另外消息重復消費的時候,讀取 Map 數據判斷 msgid 對應的執行狀態,已消費則不執行。
-
業務邏輯判斷:消息執行完會更改某個實體狀態,判斷實體狀態是否更新,如果更新,則不進行重復消費。
特別說明:AMQP 協議中的事務僅僅是指生產者發送消息給 broker 這一系列流程處理的事務機制,並不包含消費端的處理流程。 -
集群
原 RabbitMQ 集群:manager1、manager2、manager3 節點均為磁盤存儲,manager1 為主節點,HAProxy 負載三個節點。
現 RabbitMQ 集群更新(更合理的配置):
- RabbitMQ 集群更新:manager1、manager2 節點類型改為 ram(內存存儲),manager3 節點類型為 disc(磁盤存儲,用於保存集群配置和元數據),主節點變更為 manager3。
- HAProxy 負載更新:移除 manager3 負載(5672 端口),只保留 manage2、manager2 負載。
- Kafka 和 RabbitMQ 對比
Kafka 的設計有明確的介紹:http://kafka.apache.org/documentation.html#design。
Kafka 應對場景:消息持久化、吞吐量是第一要求、狀態由客戶端維護、必須是分布式的。Kafka 認為 broker 不應該阻塞生產者,高效的磁盤順序讀寫能夠和網絡 IO 一樣快,同時依賴現代 OS 文件系統特性,寫入持久化文件時並不調用 flush,僅寫入 OS pagecache,后續由 OS flush。
這些特性決定了 Kafka 沒有做“確認機制”,而是直接將生產消息順序寫入文件、消息消費后不刪除(避免文件更新),該實現充分利用了磁盤 IO,能夠達到較高的吞吐量。代價是消費者要依賴 Zookeeper 記錄隊列消費位置、處理同步問題。沒有消費確認機制,還導致了 Kafka 無法了解消費者速度,不能采用 push 模型以合理的速度向消費者推送數據,只能利用 pull 模型由消費者來拉消息(消費者承擔額外的輪詢開銷)。
如果在 Kafka 中引入消費者確認機制,就需要 broker 維護消息消費狀態,要做到高可靠就需要寫文件持久化並與生產消息同步,這將急劇降低 Kafka 的性能,這種設計也極類似 RabbitMQ。如果不改變 Kafka 的實現,而是在 Kafka 和消費者之間做一層封裝,還是需要實現一套類似 RabbitMQ 的消費確認和持久化機制。
參考資料:
https://www.zhihu.com/question/27707687
https://blog.csdn.net/varyall/article/details/79111745
http://dovov.com/RabbitMQ.html
https://blog.csdn.net/yzhou86/article/details/79156458
https://blog.csdn.net/yeweiouyang/article/details/74943278
https://blog.csdn.net/varyall/article/details/79111745
http://www.pandan.xyz/2017/03/08/RabbitMQ 事務機制/
作者:田園里的蟋蟀
微信公眾號:你好架構
出處:http://www.cnblogs.com/xishuai/