1. 程式人生 > 其它 >【RabbitMQ】常見問題

【RabbitMQ】常見問題

1、什麼是Rabbitmq

採用AMQP高階訊息佇列協議的一種訊息佇列技術,最大的特點就是消費並不需要確保提供方的存在,實現了服務之間的高度解耦

2、為什麼要使用 rabbitmq
1、在分散式系統下具備非同步,削峰,負載均衡等一系列高階功能;
2、擁有持久化的機制,程序訊息,佇列中的資訊也可以儲存下來。
3、實現消費者和生產者之間的解耦。
4、對於高併發場景下,利用訊息佇列可以使得同步訪問變為序列訪問達到一定量的限流,利於資料庫的操作。
5.可以使用訊息佇列達到非同步下單的效果,排隊中,後臺進行邏輯下單

3、使用rabbitmq的場景
1、服務間非同步通訊
2、順序消費
3、定時任務
4、請求削峰

4、如何確保訊息正確地傳送至Rabbitmq?如何確保訊息接收方消費了訊息?
傳送方確認模式
將通道設定成 confirm 模式(傳送方確認模式),則所有在通道上釋出的訊息都會被指派一個唯一的 ID。
一旦訊息被投遞到目的佇列後,或者訊息被寫入磁碟後(可持久化的訊息),通道會發送一個確認給生產者(包含訊息唯一 ID)。
如果 RabbitMQ 發生內部錯誤從而導致訊息丟失,會發送一條 nack(notacknowledged,未確認)訊息。傳送方確認模式是非同步的,生產
者應用程式在等待確認的同時,可以繼續傳送訊息。當確認訊息到達生產者應用程式,生產者應用程式的回撥方法就會被觸發來處理確認消
息。
接收方確認機制
接收方訊息確認機制
消費者接收每一條訊息後都必須進行確認(訊息接收和訊息確認是兩個不同操作)。只有消費者確認了訊息,RabbitMQ 才能安全地把訊息
從佇列中刪除。這裡並沒有用到超時機制,RabbitMQ 僅通過 Consumer 的連線中斷來確認是否需要重新發送訊息。也就是說,只要連線
不中斷,RabbitMQ 給了 Consumer 足夠長的時間來處理訊息。保證資料的最終一致性;
下面羅列幾種特殊情況
如果消費者接收到訊息,在確認之前斷開了連線或取消訂閱,RabbitMQ 會認為訊息沒有被分發,然後重新分發給下一個訂閱的消費者。
(可能存在訊息重複消費的隱患,需要去重)如果消費者接收到訊息卻沒有確認訊息,連線也未斷開,則 RabbitMQ 認為該消費者繁忙,將
不會給該消費者分發更多的訊息。

5、如何避免訊息重複投遞或重複消費?
在訊息生產時,MQ 內部針對每條生產者傳送的訊息生成一個 inner-msg-id,作為去重的依據(訊息投遞失敗並重傳),避免重複的訊息進
入佇列;
在訊息消費時,要求訊息體中必須要有一個 bizId(對於同一業務全域性唯一,如支付 ID、訂單 ID、帖子 ID 等)作為去重的依據,避免同一
條訊息被重複消費。

6、訊息基於什麼傳輸?
由於 TCP 連線的建立和銷燬開銷較大,且併發數受系統資源限制,會造成效能瓶頸。RabbitMQ 使用通道的方式來傳輸資料。通道是建立在
真實的 TCP 連線內的虛擬連線,且每條 TCP 連線上的通道數量沒有限制

7、訊息如何分發?
若該佇列至少有一個消費者訂閱,訊息將以迴圈(round-robin)的方式傳送給消費者。每條訊息只會分發給一個訂閱的消費者(前提是消
費者能夠正常處理訊息並進行確認)。
通過路由可實現多消費的功能

8、訊息怎麼路由?
訊息提供方->路由->一至多個佇列
訊息釋出到交換器時,訊息將擁有一個路由鍵(routing key),在訊息建立時設定。
通過佇列路由鍵,可以把佇列繫結到交換器上。
訊息到達交換器後,RabbitMQ 會將訊息的路由鍵與佇列的路由鍵進行匹配(針對不同的交換器有不同的路由規則);
常用的交換器主要分為一下三種
fanout:如果交換器收到訊息,將會廣播到所有繫結的佇列上
direct:如果路由鍵完全匹配,訊息就被投遞到相應的佇列
topic:可以使來自不同源頭的訊息能夠到達同一個佇列。 使用 topic 交換器時,可以使用萬用字元

9、如何確保訊息不丟失?
訊息持久化,當然前提是佇列必須持久化
RabbitMQ 確保永續性訊息能從伺服器重啟中恢復的方式是,將它們寫入磁碟上的一個持久化日誌檔案,當釋出一條永續性訊息到持久交換
器上時,Rabbit 會在訊息提交到日誌檔案後才傳送響應。
一旦消費者從持久佇列中消費了一條持久化訊息,RabbitMQ 會在持久化日誌中把這條訊息標記為等待垃圾收集。如果持久化訊息在被消費
之前 RabbitMQ 重啟,那麼 Rabbit 會自動重建交換器和佇列(以及繫結),並重新發布持久化日誌檔案中的訊息到合適的佇列。

10、使用RabbitMQ有什麼好處?
1、服務之間高度解耦
2、非同步通訊效能高
3、流量削峰

11、RabbitMQ叢集
映象叢集模式
你建立的 queue,無論元資料還是 queue 裡的訊息都會存在於多個例項上,然後每次你寫訊息到 queue 的時候,都會自動把訊息到多個實
例的 queue 裡進行訊息同步。
好處在於,你任何一個機器宕機了,沒事兒,別的機器都可以用。壞處在於,第一,這個效能開銷也太大了吧,訊息同步所有機器,導致網
絡頻寬壓力和消耗很重!第二,這麼玩兒,就沒有擴充套件性可言了,如果某個 queue 負載很重,你加機器,新增的機器也包含了這個 queue
的所有資料,並沒有辦法線性擴充套件你的 queue

12、mq 的缺點
系統可用性降低
系統引入的外部依賴越多,越容易掛掉,本來你就是 A 系統呼叫 BCD 三個系統的介面就好了,人 ABCD 四個系統好好的,沒啥問題,你偏
加個 MQ 進來,萬一MQ 掛了咋整?MQ 掛了,整套系統崩潰了,你不就完了麼。
系統複雜性提高
硬生生加個 MQ 進來,你怎麼保證訊息沒有重複消費?怎麼處理訊息丟失的情況?怎麼保證訊息傳遞的順序性?頭大頭大,問題一大堆,痛
苦不已
一致性問題
A 系統處理完了直接返回成功了,人都以為你這個請求就成功了;但是問題是,要是 BCD 三個系統那裡,BD 兩個系統寫庫成功了,結果 C
系統寫庫失敗了,咋整?你這資料就不一致了。
所以訊息佇列實際是一種非常複雜的架構,你引入它有很多好處,但是也得針對它帶來的壞處做各種額外的技術方案和架構來規避掉,最好
之後,你會發現,媽呀,系統複雜度提升了一個數量級,也許是複雜了 10 倍。但是關鍵時刻,用,還是得用的。

13、Kafka、ActiveMQ、RabbitMQ、RocketMQ 都有什麼區別?
對於吞吐量來說kafka和RocketMQ支撐高吞吐,ActiveMQ和RabbitMQ比他們低一個數量級。對於延遲量來說RabbitMQ是最低的。
1.從社群活躍度
按照目前網路上的資料,RabbitMQ 、activeM 、ZeroMQ 三者中,綜合來看,RabbitMQ 是首選。
2.持久化訊息比較
ActiveMq 和RabbitMq 都支援。持久化訊息主要是指我們機器在不可抗力因素等情況下掛掉了,訊息不會丟失的機制。
3.綜合技術實現
可靠性、靈活的路由、叢集、事務、高可用的佇列、訊息排序、問題追蹤、視覺化管理工具、外掛系統等等。
RabbitMq / Kafka 最好,ActiveMq 次之,ZeroMq 最差。當然ZeroMq 也可以做到,不過自己必須手動寫程式碼實現,程式碼量不小。尤其是
可靠性中的:永續性、投遞確認、釋出者證實和高可用性。
4.高併發
毋庸置疑,RabbitMQ 最高,原因是它的實現語言是天生具備高併發高可用的erlang 語言。
5.比較關注的比較, RabbitMQ 和 Kafka
RabbitMq 比Kafka 成熟,在可用性上,穩定性上,可靠性上, RabbitMq 勝於 Kafka (理論上)。另外,Kafka 的定位主要在日誌等方
面,
因為Kafka 設計的初衷就是處理日誌的,可以看做是一個日誌(訊息)系統一個重要元件,針對性很強,所以 如果業務方面還是建議
選擇 RabbitMq 。還有就是,Kafka 的效能(吞吐量、TPS )比RabbitMq 要高出來很多

14、如何保證高可用的?
RabbitMQ 是比較有代表性的,因為是基於主從(非分散式)做高可用性的,我們就以 RabbitMQ 為例子講解第一種 MQ 的高可用性怎麼
實現。RabbitMQ 有三種模式:單機模式、普通叢集模式、映象叢集模式。
單機模式,就是 Demo 級別的,一般就是你本地啟動了玩玩兒的?,沒人生產用單機模式普通叢集模式,意思就是在多臺機器上啟動多個
RabbitMQ 例項,每個機器啟動一個。你建立的queue,只會放在一個 RabbitMQ 例項上,但是每個例項都同步 queue 的元資料(元資料
可以認為是queue 的一些配置資訊,通過元資料,可以找到 queue 所在例項)。你消費的時候,實際上如果連線到了另外一個例項,那麼
那個例項會從 queue 所在例項上拉取資料過來。這方案主要是提高吞吐量的,就是說讓叢集中多個節點來服務某個 queue 的讀寫操作。
映象叢集模式:這種模式,才是所謂的 RabbitMQ 的高可用模式。跟普通叢集模式不一樣的是,在映象叢集模式下,你建立的 queue,無
論元資料還是 queue 裡的訊息都會存在於多個例項上,就是說,每個 RabbitMQ 節點都有這個 queue 的一個完整映象,包含 queue 的全
部資料的意思。然後每次你寫訊息到 queue 的時候,都會自動把訊息同步到多個例項的 queue 上。RabbitMQ 有很好的管理控制檯,就是
在後臺新增一個策略,這個策略是映象叢集模式的策略,指定的時候是可以要求資料同步到所有節點的,也可以要求同步到指定數量的節
點,再次建立 queue 的時候,應用這個策略,就會自動將資料同步到其他的節點上去了。這樣的話,好處在於,你任何一個機器宕機了,
沒事兒,其它機器(節點)還包含了這個 queue 的完整資料,別的 consumer 都可以到其它節點上去消費資料。壞處在於,第一,這個性
能開銷也太大了吧,訊息需要同步到所有機器上,導致網路頻寬壓力和消耗很重!RabbitMQ 一個 queue 的資料都是放在一個節點裡的,
映象叢集下,也是每個節點都放這個 queue 的完整資料

Kafka 一個最基本的架構認識:由多個 broker 組成,每個 broker 是一個節點;你建立一個 topic,這個 topic 可以劃分為多個 partition,
每個 partition 可以存在於不同的 broker 上,每個 partition 就放一部分資料。這就是天然的分散式訊息佇列,就是說一個 topic 的資料,
是分散放在多個機器上的,每個機器就放一部分資料。Kafka 0.8 以後,提供了 HA 機制,就是 replica(複製品)
副本機制。每個partition
的資料都會同步到其它機器上,形成自己的多個 replica 副本。所有 replica 會選舉一個leader 出來,那麼生產和消費都跟這個 leader 打交
道,然後其他 replica 就是 follower。寫的時候,leader 會負責把資料同步到所有 follower 上去,讀的時候就直接讀 leader 上的資料即
可。只能讀寫leader?很簡單,要是你可以隨意讀寫每個 follower,那麼就要 care 資料一致性的問題,系統複雜度太高,很容易出問題。
Kafka 會均勻地將一個 partition 的所有 replica 分佈在不同的機器上,這樣才可以提高容錯性。因為如果某個 broker 宕機了,沒事兒,那
個 broker上面的 partition 在其他機器上都
有副本的,如果這上面有某個 partition 的 leader,那麼此時會從 follower 中重新選舉一個新的 leader出來,大家繼續讀寫那個新的
leader 即可。這就有所謂的高可用性了。寫資料的時候,生產者就寫leader,然後 leader 將資料落地寫本地磁碟,接著其他 follower 自己
主動從 leader 來 pull 資料。一旦所有 follower 同步好資料了,就會發送 ack 給 leader,leader 收到所有 follower 的 ack 之後,就會返回
寫成功的訊息給生產者。(當然,這只是其中一種模式,還可以適當調整這個行為)消費的時候,只會從 leader 去讀,但是隻有當一個消
息已經被所有 follower 都同步成功返回 ack 的時候,這個訊息才會被消費者讀到

15、如何保證訊息的可靠傳輸?如果訊息丟了怎麼辦
資料的丟失問題,可能出現在生產者、MQ、消費者中
生產者丟失:生產者將資料傳送到 RabbitMQ 的時候,可能資料就在半路給搞丟了,因為網路問題啥的,都有可能。此時可以選擇用
RabbitMQ 提供的事務功能,就是生產者傳送資料之前開啟 RabbitMQ事務channel.txSelect,然後傳送訊息,如果訊息沒有成功被
RabbitMQ 接收到,那麼生產者會收到異常報錯,此時就可以回滾事務channel.txRollback,然後重試傳送訊息;如果收到了訊息,那麼可
以提交事務channel.txCommit。吞吐量會下來,因為太耗效能。所以一般來說,如果你要確保說寫RabbitMQ 的訊息別丟,可以開啟
confirm模式,在生產者那裡設定開啟confirm模式之後,你每次寫的訊息都會分配一個唯一的 id,然後如果寫入了 RabbitMQ 中,
RabbitMQ 會給你回傳一個ack訊息,告訴你說這個訊息 ok 了。如果 RabbitMQ 沒能處理這個訊息,會回撥你一個nack介面,告訴你這個
訊息接收失敗,你可以重試。而且你可以結合這個機制自己在記憶體裡維護每個訊息 id 的狀態,如果超過一定時間還沒接收到這個訊息的回
調,那麼你可以重發。事務機制和cnofirm機制最大的不同在於,事務機制是同步的,你提交一個事務之後會阻塞在那兒,但是confirm機
制是非同步的,你傳送個訊息之後就可以傳送下一個訊息,然後那個訊息RabbitMQ 接收了之後會非同步回撥你一個介面通知你這個訊息接收到
了。所以一般在生產者這塊避免資料丟失,都是用confirm機制的

MQ中丟失:就是 RabbitMQ 自己弄丟了資料,這個你必須開啟 RabbitMQ 的持久化,就是訊息寫入之後會持久化到磁碟,哪怕是
RabbitMQ 自己掛了,恢復之後會自動讀取之前儲存的資料,一般資料不會丟。設定持久化有兩個步驟:建立 queue 的時候將其設定為持
久化,這樣就可以保證 RabbitMQ 持久化 queue 的元資料,但是不會持久化 queue 裡的資料。第二個是傳送訊息的時候將訊息的
deliveryMode 設定為 2,就是將訊息設定為持久化的,此時 RabbitMQ 就會將訊息持久化到磁碟上去。必須要同時設定這兩個持久化才
行,RabbitMQ 哪怕是掛了,再次重啟,也會從磁碟上重啟恢復queue,恢復這個 queue 裡的資料。持久化可以跟生產者那邊的confirm機
制配合起來,只有訊息被持久化到磁碟之後,才會通知生產者ack了,所以哪怕是在持久化到磁碟之前,RabbitMQ 掛了,資料丟了,生產
者收不到ack,你也是可以自己重發的。

注意,哪怕是你給 RabbitMQ 開啟了持久化機制,但沒有開啟生產者訊息確認,也有一種可能,就是這個訊息寫到了 RabbitMQ
中,但是還沒來得及持久化到磁碟上,結果不巧,此時RabbitMQ 掛了,就會導致記憶體裡的一點點資料丟失

消費端丟失:你消費的時候,剛消費到,還沒處理,結果程序掛了,比如重啟了,那麼就尷尬了,RabbitMQ 認為你都消費了,這資料就丟
了。這個時候得用 RabbitMQ 提供的ack機制,簡單來說,就是你關閉 RabbitMQ 的自動ack,可以通過一個 api 來呼叫就行,然後每次你
自己程式碼裡確保處理完的時候,再在程式裡ack一把。這樣的話,如果你還沒處理完,不就沒有ack?那 RabbitMQ 就認為你還沒處理完,
這個時候 RabbitMQ 會把這個消費分配給別的 consumer 去處理,訊息是不會丟的

16、如何保證訊息的順序性
先看順序會錯亂的場景:RabbitMQ,一個queue,多個consumer,這不明顯亂了;

解決:
1、拆分多個queue,每個queue一個consumer,就是多一些queue而已,確實有點麻煩,
2、或者就一個queue就是對應一個consumer,然後這個consumer內部呼叫記憶體佇列做排隊,然後分發給底層不同的worker來處理。

17、如何解決訊息佇列的延時以及過期失效問題?訊息佇列滿了以後該怎麼處理?有幾百萬
訊息持續積壓幾小時,說說怎麼解決
訊息積壓處理辦法:臨時緊急擴容:
先修復 consumer 的問題,確保其恢復消費速度,然後將現有 cnosumer 都停掉。新建一個 topic,partition 是原來的 10 倍,臨時建立好
原先 10 倍的 queue 數量。然後寫一個臨時的分發資料的 consumer 程式,這個程式部署上去消費積壓的資料,消費之後不做耗時的處
理,直接均勻輪詢寫入臨時建立好的 10 倍數量的 queue。
接著臨時徵用 10 倍的機器來部署 consumer,每一批 consumer 消費一個臨時 queue 的資料。這種做法相當於是臨時將 queue 資源和
consumer 資源擴大 10 倍,以正常的 10 倍速度來消費資料。等快速消費完積壓資料之後,得恢復原先部署的架構,重新用原先的
consumer 機器來消費訊息。MQ中訊息失效:假設你用的是 RabbitMQ,RabbtiMQ 是可以設定過期時間的,也就是 TTL。如果訊息在
queue 中積壓超過一定的時間就會被 RabbitMQ 給清理掉,這個資料就沒了。那這就是第二個坑了。這就不是說資料會大量積壓在 mq
裡,而是大量的資料會直接搞丟。我們可以採取一個方案,就是批量重導,這個我們之前線上也有類似的場景幹過。就是大量積壓的時候,
我們當時就直接丟棄資料了,然後等過了高峰期以後,比如大家一起喝咖啡熬夜到晚上12點以後,使用者都睡覺了。這個時候我們就開始寫程
序,將丟失的那批資料,寫個臨時程式,一點一點的查出來,然後重新灌入 mq 裡面去,把白天丟的資料給他補回來。也只能是這樣了。假
設 1 萬個訂單積壓在 mq 裡面,沒有處理,其中 1000個訂單都丟了,你只能手動寫程式把那 1000 個訂單給查出來,手動發到 mq 裡去再
補一次
mq訊息佇列塊滿了:如果訊息積壓在 mq 裡,你很長時間都沒有處理掉,此時導致 mq 都快寫滿了,咋辦?這個還有別的辦法嗎?沒有,
誰讓你第一個方案執行的太慢了,你臨時寫程式,接入資料來消費,消費一個丟棄一個,都不要了,快速消費掉所有的訊息。然後走第二個
方案,到了晚上再補資料吧

18、設計MQ的思路
比如說這個訊息佇列系統,我們從以下幾個角度來考慮一下:
首先這個 mq 得支援可伸縮性吧,就是需要的時候快速擴容,就可以增加吞吐量和容量,那怎麼搞?設計個分散式的系統唄,參照一下
kafka 的設計理念,broker -> topic -> partition,每個 partition 放一個機器,就存一部分資料。如果現在資源不夠了,簡單啊,給 topic
增加 partition,然後做資料遷移,增加機器,不就可以存放更多資料,提供更高的吞吐量了?
其次你得考慮一下這個 mq 的資料要不要落地磁碟吧?那肯定要了,落磁碟才能保證別程序掛了資料就丟了。那落磁碟的時候怎麼落啊?順
序寫,這樣就沒有磁碟隨機讀寫的定址開銷,磁碟順序讀寫的效能是很高的,這就是 kafka 的思路。
其次你考慮一下你的 mq 的可用性啊?這個事兒,具體參考之前可用性那個環節講解的 kafka 的高可用保障機制。多副本 -> leader &
follower -> broker 掛了重新選舉 leader 即可對外服務。能不能支援資料 0 丟失啊?可以的,參考我們之前說的那個 kafka 資料零丟失方案

19、什麼是Message?
訊息,訊息是不具名的,它由訊息頭和訊息體組成。訊息體是不透明的,而訊息頭則由一系列的可選屬性組成,這些屬性包括 routingkey(路由鍵)、 priority(相對於其他訊息的優先權)、 delivery-mode(指出該訊息可能需要永續性儲存)等。

20、什麼是Publisher?
訊息的生產者,也是一個向交換機發布訊息的客戶端應用程式?

21、什麼是Exchange(將訊息發給佇列)
交換器,用來接收生產者訊息並將這些訊息路由伺服器的佇列中

22、什麼是Binding(訊息佇列和交換器之間的關聯)
繫結,用於訊息佇列和交換器之間的關聯。一個繫結就是基於路由鍵將交換器和訊息佇列連線起來的路由規則,所以可以將交換器理解為有繫結構成的路由表。

23、什麼是Queue?
訊息佇列,用來儲存訊息直接發給消費者。它是訊息容器,也是訊息的終點。一個訊息可以投個一個或多個佇列。訊息一直在佇列裡面,等待消費者連線這個佇列將其取走。

24、什麼是Connection?
網路連線,比如一個TCP連線

25、什麼是Channel?
通道,
多路複用連線中的一條獨立的雙向資料流通道。通道是建立在真實的 TCP 連線內地虛擬連線, AMQP 命令都是通過通道發出去的,
不管是釋出訊息、訂閱佇列還是接收訊息,這些動作都是通過通道完成。因為對於作業系統來說建立和銷燬 TCP 都是非常昂貴的開銷,所
以引入了通道的概念,以複用一條 TCP 連線

26、什麼Consumer?
訊息的消費者,表示一個從訊息佇列中取得訊息的客戶端應用程式。

27、什麼是Virtual Host?
虛擬主機,表示一批交換器,訊息佇列和相關物件,虛擬主機是共享相同的的身份認證和加密環境的獨立伺服器域。

28、什麼是Broker?
表示訊息佇列伺服器實體

29、Exchange 型別 ?
Exchange 分發訊息時根據型別的不同分發策略有區別,
目前共四種類型: direct、 fanout、topic、 headers 。 headers 匹配 AMQP 消
息的 header 而不是路由鍵,此外 headers 交換器和direct 交換器完全一致,但效能差很多,目前幾乎用不到了

30、Direct鍵(路由鍵)分佈?
Direct:訊息中的路由鍵如果和Binding key一致,交換器就將訊息發到對應的佇列中。他是完全匹配,單播模式。


31、Fanout(廣播分發)?
Fanout:
每個發到 fanout 型別交換器的訊息都會分到所有繫結的佇列上去。很像子網廣播,每臺子網內的主機都獲得了一份複製的訊息。
fanout 型別轉發訊息是最快的


32、topic 交換器(模式匹配)

topic 交換器: topic 交換器通過模式匹配分配訊息的路由鍵屬性,將路由鍵和某個模式進行匹配,此時佇列需要繫結到一個模式上。它將
路由鍵和繫結鍵的字串切分成單詞,這些單詞之間用點隔開。它同樣也會識別兩個萬用字元:符號“#” 和符號“” 。 #匹配 0 個或多個單詞,
匹配不多不少一個單詞。

29、交換器無法根據自身型別和路由鍵找到符合條件佇列時,有哪些處理?

mandatory :true 返回訊息給生產者。

mandatory: false 直接丟棄。

30、RabbitMQ佇列結構?
通常由以下兩部分組成:

rabbit_amqqueue_process:負責協議相關的訊息處理,即接收生產者的訊息、向消費者交付訊息、處理訊息的確認(包括生產端的 confirm 和消費端的 ack) 等。

backing_queue:是訊息儲存的具體形式和引擎,並向 rabbit amqqueue process 提供相關的介面以供呼叫。

31、叢集中的節點型別?
記憶體節點:ram,將變更寫入記憶體。

磁碟節點:disc,磁碟寫入操作。

RabbitMQ要求最少有一個磁碟節點。

32、叢集節點型別有幾種?
記憶體節點:儲存狀態到記憶體,但持久化的佇列和訊息還是會儲存到磁碟;

磁碟節點:儲存狀態到記憶體和磁碟,一個叢集中至少需要一個磁碟節點

33、Consumer Cancellation Notification 機制用於什麼場景?
用於保證當映象 queue 中 master 掛掉時,連線到 slave 上的 consumer 可以收到自身 consume 被取消的通知,進而可以重新執行 consume 動作從新選出的 master 出獲得訊息。若不採用該機制,連線到 slave 上的 consumer 將不會感知 master 掛掉這個事情,導致後續無法再收到新 master 廣播出來的 message 。另外,因為在映象 queue 模式下,存在將 message 進行 requeue 的可能,所以實現 consumer 的邏輯時需要能夠正確處理出現重複 message 的情況。

34、訊息傳輸保證層級?
1、 At most once:最多一次。訊息可能會丟失,單不會重複傳輸。

2、 At least once:最少一次。訊息覺不會丟失,但可能會重複傳輸。

3、 Exactly once:恰好一次,每條訊息肯定僅傳輸一次。

35、事務機制?
RabbitMQ 客戶端中與事務機制相關的方法有三個:

channel.txSelect 用於將當前的通道設定成事務模式。

channel 、txCommit 用於提交事務 。

channel 、txRollback 用於事務回滾,如果在事務提交執行之前由於 RabbitMQ 異常崩潰或者其他原因丟擲異常,通過txRollback來回滾。

36、如何避免訊息重複投遞或重複消費?
在訊息生產時,MQ內部針對每條生產者傳送的訊息生成一個inner-msg-id,作為去重和冪等的依據(訊息投遞失敗並重傳),避免重複的訊息進入佇列;在訊息消費時,要求訊息體中必須要有一個bizId(對於同一業務全域性唯一,如支付ID、訂單ID、帖子ID等)作為去重和冪等的依據,避免同一條訊息被重複消費。

這個問題針對業務場景來答分以下幾點:

1、 拿到這個訊息做資料庫的insert操作。然後給這個訊息做一個唯一主鍵,那麼就算出現重複消費的情況,就會導致主鍵衝突,避免資料庫出現髒資料。

2、 拿到這個訊息做Redis的set的操作,因為你無論set幾次結果都是一樣的,set操作本來就算冪等操作。

3、 如果上面兩種情況還不行。準備一個第三方介質,來做消費記錄。以Redis為例,給訊息分配一個全域性id,只要消費過該訊息,將<id,message>以K-V形式寫入Redis。那消費者開始消費前,先去Redis中查詢有沒消費記錄即可。

37、routing_key 和 binding_key 的最大長度是多少?
255 位元組。

38、basicReject / basicNack / basicRecover區別

channel.basicReject(deliveryTag, true);
basic.reject方法拒絕deliveryTag對應的訊息,第二個引數是否requeue,true則重新入佇列,否則丟棄或者進入死信佇列。

該方法reject後,該消費者還是會消費到該條被reject的訊息。

channel.basicNack(deliveryTag, false, true);
basic.nack方法為不確認deliveryTag對應的訊息,第二個引數是否應用於多訊息,第三個引數是否requeue,與basic.reject區別就是同時支援多個訊息,可以nack該消費者先前接收未ack的所有訊息。nack後的訊息也會被自己消費到。

channel.basicRecover(true);
basic.recover是否恢復訊息到佇列,引數是是否requeue,true則重新入佇列,並且儘可能的將之前recover的訊息投遞給其他消費者消費,而不是自己再次消費。false則訊息會重新被投遞給自己。