RabbitMQ 100% 投遞成功方案詳解

一. 生產端的可靠性投遞

1. 保障消息的成功發出

2. 保障MQ節點的成功接收

3. 發送端收到MQ節點(broker)確認應答

4. 完善的消息補償機制
複製代碼

在實際生產中,很難保障前三點的徹底可靠,好比在極端的環境中,生產者發送消息失敗了,發送端在接受確認應答時忽然發生網絡閃斷等等狀況,很難保障可靠性投遞,因此就須要有第四點完善的消息補償機制。數據庫

2、互聯網大廠的解決方案

第一種:消息落庫,對消息狀態進行達標。具體來講就是將消息持久化到數據庫並設置狀態值,收到消費端的應答就改變當前記錄的狀態。再用輪詢去從新發送沒接收到應答的消息,注意這裏要設置重試次數。

第二種:消息的延遲投遞,作二次確認,回調檢查。
複製代碼

3、消息落庫,對消息狀態進行打標

消息落庫的流程圖網絡

流程的示意圖如上所示,好比我下單成功了,這是進行 step1,對個人業務數據進行入庫,業務數據入庫完畢(這裏要特別注意必定要保證業務數據入庫)再對要發送的消息進行入庫,圖中採用了兩個數據庫,能夠根據實際業務場景來肯定是否採用兩個數據庫,若是採用了兩個數據庫,有人可能就像到了採用分佈式事務來保證數據的一致性,可是在大型互聯網中,基本不多采用事務,都是採用補償機制。

對業務數據和消息入庫完畢就進入 setp2,發送消息到 MQ 服務上,按照正常的流程就是消費者監聽到該消息,就根據惟一 id 修改該消息的狀態爲已消費,並給一個確認應答 ack 到 Listener。若是出現意外狀況,消費者未接收到或者 Listener 接收確認時發生網絡閃斷,接收不到,這時候就須要用到咱們的分佈式定時任務來從 msg 數據庫抓取那些超時了還未被消費的消息,從新發送一遍。重試機制裏面要設置重試次數限制,由於一些外部的緣由致使一直髮送失敗的,不能重試太屢次,要否則會拖垮整個服務。例如重試三次仍是失敗的,就把消息的 status 設置成 2,而後經過補償機制,人工去處理。實際生產中,這種狀況仍是比較少的,可是你不能沒有這個補償機制,要否則就作不到可靠性了。併發

要看代碼實現的能夠去看看個人這個系列:www.jianshu.com/c/c1785aa6c…異步

4、延遲投遞,作二次確認,回調檢查。

回想第一種方案,生產端既要對業務數據入庫,又要對消息數據入庫,這種設計在高併發場景下,真的合適嗎?在覈心鏈路上,每一次持久化都是須要很精心考量的,持久化一次就花費 100 - 200 毫秒,這在高併發場景下是忍受不了的。這時候須要咱們的第二種方案了,流程圖以下。分佈式

upstream Server 就是咱們的上游服務,也就是生產者,生產者將業務數據入庫成功後,生成兩條消息,一條是當即發送出去給到下游服務 downstream Server的,一條是延遲消息給到 補償服務 callback Server的。高併發

正常狀況下,下游服務監聽到這個即時的消息,會發送一條消息給到 callback Server,注意這裏不是採用第一種方案裏面的返回 ack 方式,而是發送了一條消息給回去。性能

callback Server 監聽到這個消息,知道了剛纔有一條消息消費成功了,而後把這個持久化到數據庫中,當上遊服務發送的延遲消息到達 callback Server 時,callback Server 就會去數據庫查詢,剛纔下游服務是否有處理過這個對應的消息,若是其 msg DB 裏面有這個記錄就說明這條消息是已經被消費了,若是不存在這個記錄,那麼 callback Server 就會發起一個 RPC 請求給到上游服務,告訴上游服務,你剛纔這個消息沒發送成功,須要從新發送一遍,上游服務就從新發送即時和延遲的兩條消息出去,按照以前的流程繼續走一遍。spa

雖然第二種方案也是沒法作到 100% 的可靠傳遞,在特別極端的狀況,仍是須要定時任務和補償機制進行輔助的。可是第二種方案的核心是減小數據庫操做,這個點很重要!設計

在高併發場景下,我考慮的不是百分百的可靠性了,而是考慮可用性,性能可否扛得住這個流量,因此我能減小一次數據庫操做就減小一次。我上游服務減小了一次數據庫操做,個人服務性能相對而言就提升了一些,並且又能把異步 callback Server 補償服務解耦出來。3d

5、結論

這兩種方案都是可行的,須要根據實際業務來進行選擇,大型的超高併發的場景會選擇第二種方案,普通的就採用第一種便可。

相關文章
相關標籤/搜索