RabbitMQ消息的100%投遞 頂
- 2019 年 12 月 16 日
- 筆記
生產端的可靠性投遞
- 保障消息的成功發出
- 保障MQ節點的成功接收
- 發送端收到MQ節點確認應答
- 完善的消息進行補償機制
解決方案
- 消息落庫,對消息狀態進行打標
- 消息的延遲投遞,做二次確認,回調檢查
消息落庫架構圖

上圖中BIZ DB為我們的業務庫,比方說保存訂單;MSG DB為消息庫,保存我們發送到MQ消息。如果在Step 3的時候,網路出現故障,Confirm機制沒有收到broker的消息確認。我們需要設置一個時間臨界點,比如說5分鐘,檢索出消息庫中狀態為0的消息,通過分散式定時任務,比如XXL-Job或者Elastic-Job。但有可能出現消息剛發出去,還沒有Confirm成功,定時任務就已經啟動了,並把發送成功的消息確認為未成功,所以我們需要有一個Step 6,給入庫消息一個最大的容忍時間,比如說2分鐘到5分鐘。比如消息入庫的時候需要帶上時間,我們取出狀態為0的消息形成一個集合,然後過濾該集合的時間為2分鐘以上的消息進行重新發送。由於MQ消息的配置本身有問題的情況下(比如說路由,隊列,交換機),會出現消息永遠無法發送成功,這個時候我們需要有一個消息重試的機制,比如3次,如果3次都沒有發送成功,則更新該消息狀態為2,表示失敗。
但是這種方式有一定的局限性,因為要做資料庫的二次入庫操作,磁碟io會成為瓶頸,在高並發的場景下並不合適。由於我們的業務入庫是必須的,所以我們要考慮消息入庫是否可以取消。所以我們要考慮第二種方式:消息的延遲投遞,做二次確認,回調檢查。

這裡的Upstream service為我們的上流業務,我們肯定要做的是進行業務入庫,然後再發消息到Broker,這裡的不同在於Step2,Step2需要再發一條消息,但這條消息是一個延遲消息(這種延遲消息並不是死信隊列那種延遲消息,我們可以用DelayQueue來發這條消息),可能是2到5分鐘之後才發出去的,該消息與Step1雖然消息內容一樣,但是它們投遞的隊列不同,該隊列被Call back service監聽。Downstream service為MQ消費者,但它同時也是消息投遞者,它會在Step4發送一個confirm消息(該消息也是一個MQ消息)到Broker,但是這條消息並不是發給上流生產者的,而是發給Call back service的,Call back service作為消息消費者收到這條消息,然後做一個消息的持久化存儲,存入資料庫中。3到5分鐘之後,Call back service收到延遲發送的Step2的消息,再去對比消息資料庫中的消息,更新消息資料庫中的狀態為1。如果在此過程中,Downstream service沒有發送消息給Call back service,則消息資料庫中沒有這條消息,當Call back service收到Step2的延遲消息的時候,就通過RPC或者Restful重新調用upstream中的業務庫檢查後,再重新發送一條step1消息,走下一個流程。
以上的這個方案並不能保證100%的消息投遞,但是它對於第一種方案的好處是少進行了一次DB操作,保證高並發的性能。比如說第一種方式的吞吐量為1000,那第二種方式的吞吐量就可以翻倍到2000,可以節省一台伺服器。這裡把消息補償機制從第一種方式的嵌入到核心鏈路給解耦為一個單獨的微服務,核心鏈路則只有消息發送到消息處理。同時下游業務處理應該加上消息的冪等,確保只處理一個消息。

