RabbitMQ使用時注意的一些問題


 一、前言

      上篇RabbitMQ的博文居然上了推薦,效果很不錯,接下來我們就來聊聊我們RabbitMQ的方案,先談方案,代碼等等后面補上,感覺不錯給我點點關注,點點👍,本來能早點寫完這篇博文的,由於工作最近很繁忙稍微推遲一些時間。

 二、方案

      方案從兩方面談:生產者的投遞以及消費端的消費。

      生產端

      生產端要保障的是消息發出去,RabbitMQ的Borker收到並且返會確認收到,由於網絡的原因消息發送Borker的時候可能失敗,另外Borker返回給生產者確認的時候也可能發生閃斷,所以為了保障100%投遞我們還需要合理的補償機制。主要提供兩種方案:消息打標和延時隊列,還是以最經典的下訂單的場景為例:

      消息打標:

      消息打標的意思就是將消息落庫以后,里面對消息狀態進行記錄,標記為發送中以及發送成功兩種狀態,然后采用定時輪詢任務的方式去查找消息是否發送成功,具體看下圖:

      

        接下來我們詳細介紹一下該流程:

        1.訂單生成同時落地到訂單表和消息記錄表,這里的消息記錄表可以用MongoDB或者ES等方式進行替代;

        2.從消息記錄表讀取消息,發送消息到Borker同時更改消息狀態為發送中;

        3.假設消息接收成功,Borker接收消息成功,並返回確認收到給生產者,這個時候更新消息記錄表消息狀態為成功;

        4.假設消息接收失敗,可能失敗的地方有兩個處:Borker接收消息和返回消息都可能發生網絡問題或者其他狀況,導致生產者接收不到返回值;

        5.定時任務輪詢,規定時間內沒有發送成功消息再次按照步驟2進行投遞,這個規定時間最多容忍2或者次查詢輪詢同一個條消息,如果依然接收不成功,那么則設置消息接收失敗,這里可能是交換機或者隊列綁定失敗造成的,需要我們人工排查;

        延遲隊列:

        使用延遲隊列對消息的延遲投遞,通過回調函數做二次檢查確認消息投遞成功;延遲隊列是針對消息來說,是指當消息發送出去以后不想立即被消費,而是等待特定的時間后,消費者才進行消費,比如我們常見的支付付款,30分鍾內必須付款成功,否則就異常,這里是延遲隊列的經典場景;RabbitMQ來說本身是不支持延遲隊列的,但是可以通過死信隊列和過期時間來實現延遲隊列,簡單解釋下就是生產隊發布消息到正常隊列,設置過期時間,綁定死信交換機,消費端直接消費死信隊列,這樣就完成延遲隊列的實現;通過延時隊列去實現消息100%可靠投遞的化,會涉及到消費者消費的問題,相對比較復雜;

       

       接下來我們詳細介紹通過延遲隊列實現消息100%可靠投遞的流程:

       1.當訂單落庫以后,生產者發送消息給Borker,並且發送延時消息,該消息是用來做二次檢查確認的;

       2.消費者(2)消費成功以后,將消費成功的消息體回發到Borker中(3);

       3.回調服務(4)消費Boker中消費者消費成功的消息體並且入庫;

       4.當延時隊列(5)中存在消息時候做檢查數據庫是否存在消息,如果存在着消息消費成功,如果不存在則消息消費失敗,同時再次調用生產者發送消息;

       以上就是我前面提到過的兩種方案,我們公司現在還是再用第一種 ,對於第二種主要是為了提升系統的吞吐量,減少一次入庫;其實合適思想就是通過補償,來完成消息100%投遞,這里面就存在一個問題,同一條消息可能會被投遞多次,可能照成消費端多次消費同一條消息,所以這個時候我們就要考慮客戶端冪等性的設計;另外消費端消費的時候,消息的順序是得不到保障的,如果有業務之間相互依賴就需要考慮消息順序不一致時候處理。

       消費端

       消費端消費需要注意得地方就是上面提到得兩種情況:冪等性以及消息的順序問,接下來我們也來聊一聊消費端的設計問題。

       冪等性

       冪等性這個也是我們Web端設計時候要考慮的問題之一,冪等性就是多次提交與一次提交看到的結果是相同的,這里解釋的有點簡單,感興趣的自己百度下深入了解下;這里我們來聊聊我們消費端怎么來設計實現冪等;

       1.唯一id+業務規則,利用數據庫主鍵去重,我們現在就是通過唯一id去重,業務規則具體可以根據你們的使用場景去決定你是否需要加上這個條件;

       2.通過Redis去實現冪等,通過Redis緩存去判斷該消息是否消費過,但是這個時候我們要考慮Redis與數據庫怎么要保障原子性的問題;

       業務依賴(順序性)

       這個問題其實我不太介意設計這么復雜,但是要是真是存在這樣的場景,那也聊一聊我的一些想法:

       1.保障順序消費的消息都需要在同一個隊列中,保障順序消費的消息的Id是一致的,並且消費者只能有一個;

       2.增加消息體屬性:順序消費的標記用來區分誰先消費;

       3.當消費端消費以后,如果是消費順序正確,那么落庫,如果消息順序不正確,則先落庫消息,並且發送延時消息;

       4.當收到延時隊消息的時候,然后根據消息id查詢數據庫,進行數據處理,如果還是順序不對則再次發送延時消息;

       為什么這么設計?其實不這么設計也是可以,比如我們消息順序不對的時候,直接投入延時隊列,這種不能區分到底誰先被消費,只能靠隨機去嘗試,極端情況下可能很多次都無法保證順序是一致,所以我增加順序標記的想法,為什么要保證到同一個隊列和只有一個消費者,這是為了保證順序性,當多個隊列,多個消費者的時候順序性更難保證。

       以上都是我自己的一些想法,如果發現不對,請指正謝謝!

 三、結束

       最近一段時間比較忙,代碼部分我想做一些封裝,暫時沒時間寫,等等穩定以后補充上,大家在稍微等等,接下來還會介紹一些消息存儲和鏡像隊列方面的知識,歡迎大家加群438836709!歡迎大家關注我!

       


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM