若是RabbitMQ集羣只有一個broker節點,那麼該節點的失效將致使整個服務臨時性的不可用,而且可能會致使message的丟失(尤爲是在非持久化message存儲於非持久化queue中的時候)。能夠將全部message都設置爲持久化,而且使用持久化的queue,可是這樣仍然沒法避免因爲緩存致使的問題:由於message在發送以後和被寫入磁盤並執行fsync之間存在一個雖然短暫可是會產生問題的時間窗。經過publisher的confirm機制可以確保客戶端知道哪些message已經存入磁盤,儘管如此,通常不但願遇到因單點故障致使服務不可用。node
若是RabbitMQ集羣是由多個broker節點構成的,那麼從服務的總體可用性上來說,該集羣對於單點失效是有彈性的,可是同時也須要注意:儘管exchange和binding可以在單點失效問題上倖免於難,可是queue和其上持有的message卻不行,這是由於queue及其內容僅僅存儲於單個節點之上,因此一個節點的失效表現爲其對應的queue不可用。正則表達式
舉例說明一下,若是一個MQ集羣由三個節點組成(MQ集羣節點的模式也是有講究的,通常三個節點會有一個RAM,兩個DISK),exchange、bindings 等元數據會在三個節點之間同步,但queue上的消息是不會同步的,且不特殊設置的狀況下,Queue只會在一個節點存在。可能有的同窗會提另外一個問題,我從三個MQ幾點的監控面板,均可以看到這個Queue?這個是對的,這是因爲Queue的元數據也是在三個節點之間同步,但Queue的實際存儲只會在一個節點。咱們發送消息到指定Queue,實際上是發送消息到指定節點下的Queue。以下圖所示,消息發送至隊列testQueue,不管發送者經過哪一個MQ節點執行發送,其最終的執行都會是在MQ03節點執行消息的存儲。
數據庫
說到這兒,可能有的小夥伴就要問了?說好的,RabbitMQ集羣提供高可用性呢。緩存
分析一下,RabbitMQ集羣搭建完成後,若是不進行任何高可用配置,會有哪些問題呢?架構
引入RabbitMQ的鏡像隊列機制,將queue鏡像到cluster中其餘的節點之上。在該實現下,若是集羣中的一個節點失效了,queue能自動地切換到鏡像中的另外一個節點以保證服務的可用性。在一般的用法中,針對每個鏡像隊列都包含一個master和多個slave,分別對應於不一樣的節點。slave會準確地按照master執行命令的順序進行命令執行,故slave與master上維護的狀態應該是相同的。除了publish外全部動做都只會向master發送,而後由master將命令執行的結果廣播給slave們,故看似從鏡像隊列中的消費操做其實是在master上執行的。
一旦完成了選中的slave被提高爲master的動做,發送到鏡像隊列的message將不會再丟失:publish到鏡像隊列的全部消息老是被直接publish到master和全部的slave之上。這樣一旦master失效了,message仍然能夠繼續發送到其餘slave上。負載均衡
簡單來講,鏡像隊列機制就是將隊列在三個節點之間設置主從關係,消息會在三個節點之間進行自動同步,且若是其中一個節點不可用,並不會致使消息丟失或服務不可用的狀況,提高MQ集羣的總體高可用性。學習
先來看下設置鏡像隊列後的效果: 鏡像隊列會出現+2標識。視頻
1.設置隊列爲鏡像隊列:Howblog
兩種方式:教程
rabbitmqctl set_policy [-p Vhost] Name Pattern Definition [Priority] -p Vhost: 可選參數,針對指定vhost下的queue進行設置 Name: policy的名稱 Pattern: queue的匹配模式(正則表達式) Definition:鏡像定義,包括三個部分ha-mode, ha-params, ha-sync-mode ha-mode:指明鏡像隊列的模式,有效值爲 all/exactly/nodes all:表示在集羣中全部的節點上進行鏡像 exactly:表示在指定個數的節點上進行鏡像,節點的個數由ha-params指定 nodes:表示在指定的節點上進行鏡像,節點名稱經過ha-params指定 ha-params:ha-mode模式須要用到的參數 ha-sync-mode:進行隊列中消息的同步方式,有效值爲automatic和manual priority:可選參數,policy的優先級
請注意一個事實,鏡像配置的pattern 採用的是正則表達式匹配,也就是說會匹配一組。
RabbitMQ集羣節點失效,MQ處理策略:
若是某個slave失效了,系統處理作些記錄外幾乎啥都不作:master依舊是master,客戶端不須要採起任何行動,或者被通知slave失效。
若是master失效了,那麼slave中的一個必須被選中爲master。被選中做爲新的master的slave一般是最老的那個,由於最老的slave與前任master之間的同步狀態應該是最好的。然而,特殊狀況下,若是存在沒有任何一個slave與master徹底同步的狀況,那麼前任master中未被同步的消息將會丟失。
鏡像隊列消息的同步:
將新節點加入已存在的鏡像隊列時,默認狀況下ha-sync-mode=manual,鏡像隊列中的消息不會主動同步到新節點,除非顯式調用同步命令。當調用同步命令後,隊列開始阻塞,沒法對其進行操做,直到同步完畢。當ha-sync-mode=automatic時,新加入節點時會默認同步已知的鏡像隊列。因爲同步過程的限制,因此不建議在生產的active隊列(有生產消費消息)中操做。
rabbitmqctl list_queues name slave_pids synchronised_slave_pids 查看那些slaves已經完成同步
rabbitmqctl sync_queue name 手動的方式同步一個queue
rabbitmqctl cancel_sync_queue name 取消某個queue的同步功能
以上針對消息同步的命令,都可以經過監控界面來進行操做,最終也是經過這些操做命令執行。
說明:
鏡像隊列中某個節點宕掉的後果:
當slave宕掉了,除了與slave相連的客戶端鏈接所有斷開以外,沒有其餘影響。
當master宕掉時,會有如下連鎖反應:
1. 與master相連的客戶端鏈接所有斷開;
2.選舉最老的slave節點爲master。若此時全部slave處於未同步狀態,則未同步部分消息丟失;
3.新的master節點requeue全部unack消息,由於這個新節點沒法區分這些unack消息是否已經到達客戶端,亦或是ack消息丟失在老的master的鏈路上,亦或者是丟在master組播ack消息到全部slave的鏈路上。因此處於消息可靠性的考慮,requeue全部unack的消息。此時客戶端可能有重複消息;
4.若是客戶端連着slave,而且Basic.Consume消費時指定了x-cancel-on-ha-failover參數,那麼客戶端會受到一個Consumer Cancellation Notification通知。若是未指定x-cancal-on-ha-failover參數,那麼消費者就沒法感知master宕機,會一直等待下去。
這就告訴咱們,集羣中存在鏡像隊列時,從新master節點有風險。
鏡像隊列中節點啓動順序,很是有講究:
假設集羣中包含兩個節點,通常生產環境會部署三個節點,但爲了方便說明,採用兩個節點的形式進行說明。
場景1:A先停,B後停
該場景下B是master,只要先啓動B,再啓動A便可。或者先啓動A,再在30s以內啓動B便可恢復鏡像隊列。(若是沒有在30s內回覆B,那麼A本身就停掉本身)
場景2:A,B同時停
該場景下多是由掉電等緣由形成,只需在30s內聯繫啓動A和B便可恢復鏡像隊列。
場景3:A先停,B後停,且A沒法恢復。
由於B是master,因此等B起來後,在B節點上調用rabbitmqctl forget_cluster_node A以接觸A的cluster關係,再將新的slave節點加入B便可從新恢復鏡像隊列。
場景4:A先停,B後停,且B沒法恢復
該場景比較難處理,舊版本的RabbitMQ沒有有效的解決辦法,在如今的版本中,由於B是master,因此直接啓動A是不行的,當A沒法啓動時,也就沒版本在A節點上調用rabbitmqctl forget_cluster_node B了,新版本中forget_cluster_node支持-offline參數,offline參數容許rabbitmqctl在離線節點上執行forget_cluster_node命令,迫使RabbitMQ在未啓動的slave節點中選擇一個做爲master。當在A節點執行rabbitmqctl forget_cluster_node -offline B時,RabbitMQ會mock一個節點表明A,執行forget_cluster_node命令將B提出cluster,而後A就能正常啓動了。最後將新的slave節點加入A便可從新恢復鏡像隊列
場景5:A先停,B後停,且A和B均沒法恢復,可是能獲得A或B的磁盤文件
這個場景更加難以處理。將A或B的數據庫文件($RabbitMQ_HOME/var/lib目錄中)copy至新節點C的目錄下,再將C的hostname改爲A或者B的hostname。若是copy過來的是A節點磁盤文件,按場景4處理,若是拷貝過來的是B節點的磁盤文件,按場景3處理。最後將新的slave節點加入C便可從新恢復鏡像隊列。
場景6:A先停,B後停,且A和B均沒法恢復,且沒法獲得A和B的磁盤文件
無解。
啓動順序中有一個30s 的概念,這個是MQ 的時間間隔,用於檢測master、slave是否可用,所以30s 很是關鍵。
對於生產環境MQ集羣的重啓操做,須要分析具體的操做順序,不可無序的重啓,會有可能帶來沒法彌補的傷害(數據丟失、節點沒法啓動)。
簡單總結下:鏡像隊列是用於節點之間同步消息的機制,避免某個節點宕機而致使的服務不可用或消息丟失,且針對排他性隊列設置是無效的。另外很重要的一點,鏡像隊列機制不是負載均衡。
歡迎工做一到五年的Java工程師朋友們加入Java架構開發:855801563 獲取更多免費視頻教程。
合理利用本身每一分每一秒的時間來學習提高本身,不要再用"沒有時間「來掩飾本身思想上的懶惰!趁年輕,使勁拼,給將來的本身一個交代