RabbitMQ高級特性(RabbitMQ學習筆記 3)

一、如何保障消息100%投遞成功?

1.1 消息生產者的如何做到可靠性投遞?

消息的生產者想要確保消息成功發出並且被接收需要做到以下四個方面:

  1. 保障消息的成功發出
  2. 保障MQ節點的成功接收
  3. 發送端收到MQ節點(Broker)確認應答 完
  4. 善的消息進行補償機制

解決方案:

  1. 消息消息落庫,對消息的狀態進行打標
    這種方法是將消息持久化到數據庫中,並設置一個字段對消息的狀態進行標識(如發送中、發送成功、發送失敗)。然後遍歷所有消息對於發送失敗的消息重新發送,同時設置一定的重試次數限制,達到限制就不再重試。(注意一定是先將消息持久化到數據庫再發送消息)

  2. 消息延遲投遞,做二次確認回調檢查
    生成端生成一條消息發送到MQ,同時發送一條延遲的消息(如延遲3分鐘發送),消費端消費成功這條消息之後,會發送一條消息確認已經消費完成。此時Callback服務監聽消費端發出的消息,然後將消息持久化到數據庫中。當延遲投遞的消息發送到MQ,Callback服務監聽到延遲發送的消息,並根據庫所存儲的消息的狀態進行判斷,判斷消息是否正常的走完了所有的流程。如果確認成功那就結束,如果存在問題那麼就會發送Resend Command到消息生產端要求重新發送消息。
    在這裏插入圖片描述

二、冪等性

簡單來說冪等性就是在高併發操作時,不會發生混亂。如電影院售票總共有一百張票,當100個人同時買票時,每個線程都要對票的總數減1,所謂混亂就是每個線程都是查詢到總票數爲100然後減1,這樣所有線程操作完剩餘的票數就是99,這顯然是不對的。冪等性保障就是爲了避免這種情況的發生,最後剩餘票數爲0纔是我們想要的結果。

2.1 消息消費端的冪等性保障

  1. 唯一ID+指紋碼機制,利用數據庫主鍵去重
    首先利用Select 語句查詢 select count(1) from table where ID = 唯一ID + 指紋碼 , 如果查詢出來沒有結果說明該消息還沒有被消費,就可以進行消費。
    注意: 這裏的指紋碼跟指紋沒有什麼關係,他指的就是一種自定的規則,可以是時間戳加上一些串碼。
    好處: 實現簡單
    壞處: 高併發下會有數據庫寫入的性能瓶頸
    解決方案: 根據ID進行算法路由,分庫分表
  2. 利用Redis的原子性
    將消息存儲在Redis當中,我們可以利用 Redis的 hasKey() 來判斷消息是否已經消費過了。
    使用Redis我們需要考慮兩個問題:
    問題一:是否要進行數據庫落庫,如果落庫如何保證數據庫和Redis中的數據一致性,比如Redis存儲成功,但是寫入數據庫時失敗了。這樣就會出現問題,所有要做到同時成功同時失敗。
    問題二:如果不實時的進行落庫,那麼就需要考慮如果設置定時同步策略,畢竟數據存在數據庫中還是更穩妥一點。

三、Confirm確認消息

Confirm消息確認機制指的是生產者投遞消息後,如果Broker收到消息,則會給生產者一個應答。生產者進行接收應答,用來確定這條消息是否正常的發送到Broker,這種方式也是消息的可靠性投遞的核心保障。
在這裏插入圖片描述
實現流程:
第一步:在channel上開啓確認模式:channel.confirmSelect()
第二步:在channel上添加監聽:addConfirmListener,監聽成功和失敗的返回結果,根據具體的結果對消息進行重新發送、或記錄日誌等後續處理!

四、Return消息機制

Return Listener 用於處理一些不可路由的消息!
一般情況下我們的消息生產者,通過指定一個Exchange和Routingkey,把消息送達到某一個隊列中去,然後我們的消費者監聽隊列,進行消費處理操作!但是在某些情況下,如果我們在發送消息的時候,當前的exchange不存在或者指定的路由key路由不到,這個時候如果我們需要監聽這種不可達的消息,就要使用Return Listener。

使用Return消息機制時我們需要注意一個屬性的設置:
Mandatory: 如果爲true,則監聽器會接收到路由不可達的消息,然後進行後續處理,如果爲false,那麼broker端自動刪除該消息,所以如果要使用我們需要設置爲true。
在這裏插入圖片描述

五、消費端的自定義監聽

繼承DefaultConsumer類,重寫裏面的handleDelivery即可。不需要再使用循環的方式監聽隊列。

六、消費端限流

我們假設一個場景,在RabbitMQ服務器有上萬條未處理的消息,這時我們隨便打開一個消費者客戶端,就會出現如下情況:巨量的消息瞬間全部推送過來,但是單個客戶端無法同時處理這麼多數據,這就可能會導致服務器崩潰出現故障,所以限流就十分重要。
RabbitMQ提供了一種qos(服務質量保證)功能,即在非自動確認消息的前提下,如果一定數目的消息(通過基於consume或者channel設置Qos的值)未被確認前,不進行消費新的消息。(注:要實現限流一定要將自動簽收aotoACK設置爲false)

七、消費端手動ACK與重回隊列

消費端手動ACK與NACK:
當消費端進行消費時,由於一些異常的情況可能導致消費不成功,這時就要發送NACK,讓消息重新發送一次重新處理。但是如果達到一定的重試次數還是沒有成功那麼我就需要手動的發送ACK進行簽收,然後可以使用日誌將有問題的消息進行記錄,後續再人工進行干預處理。
重回隊列(requene):
重回隊列就是把沒有處理成功的消息重新放到隊列的尾部。不過在實際使用中,一般都不會使用重回隊列。

七、TTL

TTL是Time To Live的縮寫,也就是生存時間
RabbitMQ支持消息的過期時間,從消息發出開始計算
RabbitMQ支持隊列的過期時間,從消息進入隊列開始計算,只要超過了隊列的超時時間配置,那麼消息會自動的清除

八、死信隊列

死信隊列:DLX,Dead-Letter-Exchange
當消息在一個隊列中變成死信(dead message)之後,它能被重新publish到另一個Exchange,這個Exchange就是死信隊列。死信隊列也是一個正常的Exchange,和一般的Exchange沒有區別,它能在任意的隊列上被指定。當這個隊列中有死信時,RabbitMQ就會自動的將這個消息重新發布到設置的死信隊列上去,進而被路由到另一個隊列。(注意:死信隊列並不是一個Quene而是一個Exchange)

消息變爲死信的情況:

  1. 消息被拒絕(basic.reject/basic.nack),並且requeue=false 也就是重回隊列被設置爲false
  2. 消息TTL過期
  3. 隊列達到最大長度

聲明死信隊列:

  1. 聲明一個Exchange 如 dlx.exchange
  2. 聲明一個Quene 如 dlx.quene
  3. 聲明RouteKey 爲 # 也就是所有的消息都可以被路由到 dlx.quene中
  4. 在聲明我們要使用的消費端隊列時,添加一個Map參數 arguements
    arguements.put(“x-dead-letter-exchange”,“dlx.exchange”),這樣一來該隊列就會把隊列中的死信發送到 dlx.exchange上。
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章