問題一:RabbitMQ 中的 broker 是指什么?cluster 又是指什么?
答:broker 是指一個或多個 erlang node 的邏輯分組,且 node 上運行著 RabbitMQ 應(yīng)用
程序。cluster 是在 broker 的基礎(chǔ)之上,增加了 node 之間共享元數(shù)據(jù)的約束。
問題二:什么是元數(shù)據(jù)?元數(shù)據(jù)分為哪些類型?包括哪些內(nèi)容?與 cluster 相關(guān)的元數(shù)據(jù)
有哪些?元數(shù)據(jù)是如何保存的?元數(shù)據(jù)在 cluster 中是如何分布的?
答:在非 cluster 模式下,元數(shù)據(jù)主要分為 Queue 元數(shù)據(jù)(queue 名字和屬性等)、
Exchange 元數(shù)據(jù)(exchange 名字、類型和屬性等)、Binding 元數(shù)據(jù)(存放路由關(guān)系的查
找表)、Vhost 元數(shù)據(jù)(vhost 范圍內(nèi)針對前三者的名字空間約束和安全屬性設(shè)置)。在
cluster 模式下,還包括 cluster 中 node 位置信息和 node 關(guān)系信息。元數(shù)據(jù)按照 erlang
node 的類型確定是僅保存于 RAM 中,還是同時保存在 RAM 和 disk 上。元數(shù)據(jù)在
cluster 中是全 node 分布的。
問題三:RAM node 和 disk node 的區(qū)別?
答:RAM node 僅將 fabric(即 queue、exchange 和 binding 等 RabbitMQ 基礎(chǔ)構(gòu)件)相
關(guān)元數(shù)據(jù)保存到內(nèi)存中,但 disk node 會在內(nèi)存和磁盤中均進(jìn)行存儲。RAM node 上唯一
會存儲到磁盤上的元數(shù)據(jù)是 cluster 中使用的 disk node 的地址。要求在 RabbitMQ cluster
中至少存在一個 disk node 。
問題四:RabbitMQ 上的一個 queue 中存放的 message 是否有數(shù)量限制?
答:可以認(rèn)為是無限制,因為限制取決于機(jī)器的內(nèi)存,但是消息過多會導(dǎo)致處理效率的下
降。
問題五:RabbitMQ 概念里的 channel、exchange 和 queue 這些東東是邏輯概念,還是對應(yīng)著進(jìn)程實體?這些東東分別起什么作用?
答:queue 具有自己的 erlang 進(jìn)程;exchange 內(nèi)部實現(xiàn)為保存 binding 關(guān)系的查找表;
channel 是實際進(jìn)行路由工作的實體,即負(fù)責(zé)按照 routing_key 將 message 投遞給
queue 。由 AMQP 協(xié)議描述可知,channel 是真實 TCP 連接之上的虛擬連接,所有
AMQP 命令都是通過 channel 發(fā)送的,且每一個 channel 有唯一的 ID。一個 channel 只
能被單獨一個操作系統(tǒng)線程使用,故投遞到特定 channel 上的 message 是有順序的。但
一個操作系統(tǒng)線程上允許使用多個 channel 。channel 號為 0 的 channel 用于處理所有
對于當(dāng)前 connection 全局有效的幀,而 1-65535 號 channel 用于處理和特定 channel 相
關(guān)的幀。AMQP 協(xié)議給出的 channel 復(fù)用模型如下
其中每一個 channel 運行在一個獨立的線程上,多線程共享同一個 socket。
問題六:vhost 是什么?起什么作用?
答:vhost 可以理解為虛擬 broker ,即 mini-RabbitMQ server。其內(nèi)部均含有獨立的
queue、exchange 和 binding 等,但最最重要的是,其擁有獨立的權(quán)限系統(tǒng),可以做到
vhost 范圍的用戶控制。當(dāng)然,從 RabbitMQ 的全局角度,vhost 可以作為不同權(quán)限隔離
的手段(一個典型的例子就是不同的應(yīng)用可以跑在不同的 vhost 中)。
【cluster 相關(guān)】
問題七:在單 node 系統(tǒng)和多 node 構(gòu)成的 cluster 系統(tǒng)中聲明 queue、exchange ,以及
進(jìn)行 binding 會有什么不同?
答:當(dāng)你在單 node 上聲明 queue 時,只要該 node 上相關(guān)元數(shù)據(jù)進(jìn)行了變更,你就會
得到 Queue.Declare-ok 回應(yīng);而在 cluster 上聲明 queue ,則要求 cluster 上的全部
node 都要進(jìn)行元數(shù)據(jù)成功更新,才會得到 Queue.Declare-ok 回應(yīng)。另外,若 node 類型
為 RAM node 則變更的數(shù)據(jù)僅保存在內(nèi)存中,若類型為 disk node 則還要變更保存在磁盤
上的數(shù)據(jù)。
問題八:客戶端連接到 cluster 中的任意 node 上是否都能正常工作?
答:是的??蛻舳烁杏X不到有何不同。
問題九:若 cluster 中擁有某個 queue 的 owner node 失效了,且該 queue 被聲明具有
durable 屬性,是否能夠成功從其他 node 上重新聲明該 queue ?
答:不能,在這種情況下,將得到 404 NOT_FOUND 錯誤。只能等 queue 所屬的 node
恢復(fù)后才能使用該 queue 。但若該 queue 本身不具有 durable 屬性,則可在其他 node
上重新聲明。
問題十:cluster 中 node 的失效會對 consumer 產(chǎn)生什么影響?若是在 cluster 中創(chuàng)建了
mirrored queue ,這時 node 失效會對 consumer 產(chǎn)生什么影響?
答:若是 consumer 所連接的那個 node 失效(無論該 node 是否為 consumer 所訂閱
queue 的 owner node),則 consumer 會在發(fā)現(xiàn) TCP 連接斷開時,按標(biāo)準(zhǔn)行為執(zhí)行重連
邏輯,并根據(jù)“Assume Nothing”原則重建相應(yīng)的 fabric 即可。若是失效的 node 為
consumer 訂閱 queue 的 owner node,則 consumer 只能通過 Consumer Cancellation
Notification 機(jī)制來檢測與該 queue 訂閱關(guān)系的終止,否則會出現(xiàn)傻等卻沒有任何消息來
到的問題。
問題十一:能夠在地理上分開的不同數(shù)據(jù)中心使用 RabbitMQ cluster 么?
答:不能。第一,你無法控制所創(chuàng)建的 queue 實際分布在 cluster 里的哪個 node 上(一
般使用 HAProxy + cluster 模型時都是這樣),這可能會導(dǎo)致各種跨地域訪問時的常見問
題;第二,Erlang 的 OTP 通信框架對延遲的容忍度有限,這可能會觸發(fā)各種超時,導(dǎo)致
業(yè)務(wù)疲于處理;第三,在廣域網(wǎng)上的連接失效問題將導(dǎo)致經(jīng)典的“腦裂”問題,而
RabbitMQ 目前無法處理(該問題主要是說 Mnesia)。
【綜合問題】
問題十二:為什么 heavy RPC 的使用場景下不建議采用 disk node ?
答:heavy RPC 是指在業(yè)務(wù)邏輯中高頻調(diào)用 RabbitMQ 提供的 RPC 機(jī)制,導(dǎo)致不斷創(chuàng)建、
銷毀 reply queue ,進(jìn)而造成 disk node 的性能問題(因為會針對元數(shù)據(jù)不斷寫盤)。所以
在使用 RPC 機(jī)制時需要考慮自身的業(yè)務(wù)場景。
問題十三:向不存在的 exchange 發(fā) publish 消息會發(fā)生什么?向不存在的 queue 執(zhí)行
consume 動作會發(fā)生什么?
答:都會收到 Channel.Close 信令告之不存在(內(nèi)含原因 404 NOT_FOUND)。
問題十四:routing_key 和 binding_key 的最大長度是多少?
答:255 字節(jié)。
問題十五:RabbitMQ 允許發(fā)送的 message 最大可達(dá)多大?
答:根據(jù) AMQP 協(xié)議規(guī)定,消息體的大小由 64-bit 的值來指定,所以你就可以知道到底
能發(fā)多大的數(shù)據(jù)了。
問題十六:什么情況下 producer 不主動創(chuàng)建 queue 是安全的?
答:1.message 是允許丟失的;2.實現(xiàn)了針對未處理消息的 republish 功能(例如采用
Publisher Confirm 機(jī)制)。
問題十七:“dead letter”queue 的用途?
答:當(dāng)消息被 RabbitMQ server 投遞到 consumer 后,但 consumer 卻通過 Basic.Reject
進(jìn)行了拒絕時(同時設(shè)置 requeue=false),那么該消息會被放入“dead letter”queue 中。
該 queue 可用于排查 message 被 reject 或 undeliver 的原因。
問題十八:為什么說保證 message 被可靠持久化的條件是 queue 和 exchange 具有
durable 屬性,同時 message 具有 persistent 屬性才行?
答:binding 關(guān)系可以表示為 exchange – binding – queue 。從文檔中我們知道,若要求
投遞的 message 能夠不丟失,要求 message 本身設(shè)置 persistent 屬性,要求 exchange
和 queue 都設(shè)置 durable 屬性。其實這問題可以這么想,若 exchange 或 queue 未設(shè)置
durable 屬性,則在其 crash 之后就會無法恢復(fù),那么即使 message 設(shè)置了 persistent 屬
性,仍然存在 message 雖然能恢復(fù)但卻無處容身的問題;同理,若 message 本身未設(shè)置
persistent 屬性,則 message 的持久化更無從談起。
問題十九:什么情況下會出現(xiàn) blackholed 問題?
答:blackholed 問題是指,向 exchange 投遞了 message ,而由于各種原因?qū)е略?/p>
message 丟失,但發(fā)送者卻不知道??蓪?dǎo)致 blackholed 的情況:1.向未綁定 queue 的
exchange 發(fā)送 message;2.exchange 以 binding_key key_A 綁定了 queue queue_A,但向
該 exchange 發(fā)送 message 使用的 routing_key 卻是 key_B。
問題二十:如何防止出現(xiàn) blackholed 問題?
答:沒有特別好的辦法,只能在具體實踐中通過各種方式保證相關(guān) fabric 的存在。另外,
如果在執(zhí)行 Basic.Publish 時設(shè)置 mandatory=true ,則在遇到可能出現(xiàn) blackholed 情況
時,服務(wù)器會通過返回 Basic.Return 告之當(dāng)前 message 無法被正確投遞(內(nèi)含原因 312
NO_ROUTE)。
問題二十一:Consumer Cancellation Notification 機(jī)制用于什么場景?
答:用于保證當(dāng)鏡像 queue 中 master 掛掉時,連接到 slave 上的 consumer 可以收到自
身 consume 被取消的通知,進(jìn)而可以重新執(zhí)行 consume 動作從新選出的 master 出獲得
消息。若不采用該機(jī)制,連接到 slave 上的 consumer 將不會感知 master 掛掉這個事
情,導(dǎo)致后續(xù)無法再收到新 master 廣播出來的 message 。另外,因為在鏡像 queue 模
式下,存在將 message 進(jìn)行 requeue 的可能,所以實現(xiàn) consumer 的邏輯時需要能夠正
確處理出現(xiàn)重復(fù) message 的情況。
問題二十二:Basic.Reject 的用法是什么?
答:該信令可用于 consumer 對收到的 message 進(jìn)行 reject 。若在該信令中設(shè)置
requeue=true,則當(dāng) RabbitMQ server 收到該拒絕信令后,會將該 message 重新發(fā)送到下
一個處于 consume 狀態(tài)的 consumer 處(理論上仍可能將該消息發(fā)送給當(dāng)前
consumer)。若設(shè)置 requeue=false ,則 RabbitMQ server 在收到拒絕信令后,將直接將該
message 從 queue 中移除。
另外一種移除 queue 中 message 的小技巧是,consumer 回復(fù) Basic.Ack 但不對獲取到的
message 做任何處理。
而 Basic.Nack 是對 Basic.Reject 的擴(kuò)展,以支持一次拒絕多條 message 的能力。
問題二十三:為什么不應(yīng)該對所有的 message 都使用持久化機(jī)制?
答:首先,必然導(dǎo)致性能的下降,因為寫磁盤比寫 RAM 慢的多,message 的吞吐量可能
有 10 倍的差距。其次,message 的持久化機(jī)制用在 RabbitMQ 的內(nèi)置 cluster 方案時會
出現(xiàn)“坑爹”問題。矛盾點在于,若 message 設(shè)置了 persistent 屬性,但 queue 未設(shè)置
durable 屬性,那么當(dāng)該 queue 的 owner node 出現(xiàn)異常后,在未重建該 queue 前,發(fā)
往該 queue 的 message 將被 blackholed ;若 message 設(shè)置了 persistent 屬性,同時
queue 也設(shè)置了 durable 屬性,那么當(dāng) queue 的 owner node 異常且無法重啟的情況
下,則該 queue 無法在其他 node 上重建,只能等待其 owner node 重啟后,才能恢復(fù)
該 queue 的使用,而在這段時間內(nèi)發(fā)送給該 queue 的 message 將被 blackholed 。所
以,是否要對 message 進(jìn)行持久化,需要綜合考慮性能需要,以及可能遇到的問題。若想
達(dá)到 100,000 條/秒以上的消息吞吐量(單 RabbitMQ 服務(wù)器),則要么使用其他的方式來
確保 message 的可靠 delivery ,要么使用非??焖俚拇鎯ο到y(tǒng)以支持全持久化(例如使
用 SSD)。另外一種處理原則是:僅對關(guān)鍵消息作持久化處理(根據(jù)業(yè)務(wù)重要程度),且應(yīng)
該保證關(guān)鍵消息的量不會導(dǎo)致性能瓶頸。
問題二十四:RabbitMQ 中的 cluster、mirrored queue,以及 warrens 機(jī)制分別用于解決什么問題?存在哪些問題?
答:cluster 是為了解決當(dāng) cluster 中的任意 node 失效后,producer 和 consumer 均可以
通過其他 node 繼續(xù)工作,即提高了可用性;另外可以通過增加 node 數(shù)量增加 cluster
的消息吞吐量的目的。cluster 本身不負(fù)責(zé) message 的可靠性問題(該問題由 producer 通
過各種機(jī)制自行解決);cluster 無法解決跨數(shù)據(jù)中心的問題(即腦裂問題)。另外,在
cluster 前使用 HAProxy 可以解決 node 的選擇問題,即業(yè)務(wù)無需知道 cluster 中多個
node 的 ip 地址??梢岳?HAProxy 進(jìn)行失效 node 的探測,可以作負(fù)載均衡。下圖為
HAProxy + cluster 的模型。
Mirrored queue 是為了解決使用 cluster 時所創(chuàng)建的 queue 的完整信息僅存在于單一
node 上的問題,從另一個角度增加可用性。若想正確使用該功能,需要保證:1.consumer
需要支持 Consumer Cancellation Notification 機(jī)制;2.consumer 必須能夠正確處理重復(fù)
message 。
Warrens 是為了解決 cluster 中 message 可能被 blackholed 的問題,即不能接受
producer 不停 republish message 但 RabbitMQ server 無回應(yīng)的情況。Warrens 有兩種構(gòu)
成方式,一種模型是兩臺獨立的 RabbitMQ server + HAProxy ,其中兩個 server 的狀態(tài)分
別為 active 和 hot-standby 。該模型的特點為:兩臺 server 之間無任何數(shù)據(jù)共享和協(xié)議
交互,兩臺 server 可以基于不同的 RabbitMQ 版本。如下圖所示
另一種模型為兩臺共享存儲的 RabbitMQ server + keepalived ,其中兩個 server 的狀態(tài)分
別為 active 和 cold-standby 。該模型的特點為:兩臺 server 基于共享存儲可以做到完全
恢復(fù),要求必須基于完全相同的 RabbitMQ 版本。如下圖所示
Warrens 模型存在的問題:對于第一種模型,雖然理論上講不會丟失消息,但若在該模型
上使用持久化機(jī)制,就會出現(xiàn)這樣一種情況,即若作為 active 的 server 異常后,持久化
在該 server 上的消息將暫時無法被 consume ,因為此時該 queue 將無法在作為 hot-
standby 的 server 上被重建,所以,只能等到異常的 active server 恢復(fù)后,才能從其上的
queue 中獲取相應(yīng)的 message 進(jìn)行處理。而對于業(yè)務(wù)來說,需要具有:a.感知 AMQP 連
接斷開后重建各種 fabric 的能力;b.感知 active server 恢復(fù)的能力;c.切換回 active
server 的時機(jī)控制,以及切回后,針對 message 先后順序產(chǎn)生的變化進(jìn)行處理的能力。
對于第二種模型,因為是基于共享存儲的模式,所以導(dǎo)致 active server 異常的條件,可能
同樣會導(dǎo)致 cold-standby server 異常;另外,在該模型下,要求 active 和 cold-standby
的 server 必須具有相同的 node 名和 UID ,否則將產(chǎn)生訪問權(quán)限問題;最后,由于該模
型是冷備方案,故無法保證 cold-standby server 能在你要求的時限內(nèi)成功啟動。