由於RabbitMQ是用erlang開發的,RabbitMQ完全依賴Erlang的Cluster,因為erlang天生就是一門分布式語言,集群非常方便,但其本身並不支持負載均衡。Erlang的集群中各節點是經由過程一個magic cookie來實現的,這個cookie存放在 $home/.erlang.cookie 中(像我的root用戶安裝的就是放在我的root/.erlang.cookie中),文件是400的權限。所以必須包管各節點cookie對峙一致,不然節點之間就無法通信。
RabbitMQ的集群節點包括內存節點、磁盤節點。顧名思義內存節點就是將所有數據放在內存,磁盤節點將數據放在磁盤。不過,如果在投遞消息時,打開了消息的持久化,那么即使是內存節點,數據還是安全的放在磁盤。
Rabbitmq集群大概分為二種方式:
(1)普通模式:默認的集群模式。
(2)鏡像模式:把需要的隊列做成鏡像隊列。
一個rabbitmq集 群中可以共享 user,vhost,queue,exchange等,所有的數據和狀態都是必須在所有節點上復制的,一個例外是,那些當前只屬於創建它的節點的消息隊列,盡管它們可見且可被所有節點讀取。rabbitmq節點可以動態的加入到集群中,一個節點它可以加入到集群中,也可以從集群環境中移除。
集群中有兩種節點:
1 內存節點:只保存狀態到內存(一個例外的情況是:持久的queue的持久內容將被保存到disk)
2 磁盤節點:保存狀態到內存和磁盤。
內存節點雖然不寫入磁盤,但是它執行比磁盤節點要好。集群中,只需要一個磁盤節點來保存狀態 就足夠了如果集群中只有內存節點,那么不能停止它們,否則所有的狀態,消息等都會丟失。
良好的設計架構可以如下:在一個集群里,有3台以上機器,其中1台使用磁盤模式,其它使用內存模式。其它幾台為內存模式的節點,無疑速度更快,因此客戶端(consumer、producer)連接訪問它們。而磁盤模式的節點,由於磁盤IO相對較慢,因此僅作數據備份使用。
一、普通模式:默認的集群模式
默認的集群模式,queue創建之后,如果沒有其它policy,則queue就會按照普通模式集群。對於Queue來說,消息實體只存在於其中一個節點,A、B兩個節點僅有相同的元數據,即隊列結構,但隊列的元數據僅保存有一份,即創建該隊列的rabbitmq節點(A節點),當A節點宕機,你可以去其B節點查看,./rabbitmqctl list_queues 發現該隊列已經丟失,但聲明的exchange還存在。當消息進入A節點的Queue中后,consumer從B節點拉取時,RabbitMQ會臨時在A、B間進行消息傳輸,把A中的消息實體取出並經過B發送給consumer,所以consumer應平均連接每一個節點,從中取消息。該模式存在一個問題就是當A節點故障后,B節點無法取到A節點中還未消費的消息實體。如果做了隊列持久化或消息持久化,那么得等A節點恢復,然后才可被消費,並且在A節點恢復之前其它節點不能再創建A節點已經創建過的持久隊列;如果沒有持久化的話,消息就會失丟。這種模式更適合非持久化隊列,只有該隊列是非持久的,客戶端才能重新連接到集群里的其他節點,並重新創建隊列。假如該隊列是持久化的,那么唯一辦法是將故障節點恢復起來。
為什么RabbitMQ不將隊列復制到集群里每個節點呢?這與它的集群的設計本意相沖突,集群的設計目的就是增加更多節點時,能線性的增加性能(CPU、內存)和容量(內存、磁盤)。理由如下
1。存儲空間:如果每個集群節點每個隊列的一個完整副本,增加節點需要更多的存儲容量。例如,如果一個節點可以存儲1 gb的消息,添加兩個節點需要兩份相同的1 gb的消息
2。性能:發布消息需要將這些信息復制到每個集群節點。對持久消息,要求為每條消息觸發磁盤活動在所有節點上。每次添加一個節點都會帶來 網絡和磁盤的負載。
當然RabbitMQ新版本集群也支持隊列復制(有個選項可以配置)。比如在有五個節點的集群里,可以指定某個隊列的內容在2個節點上進行存儲,從而在性能與高可用性之間取得一個平衡(應該就是指鏡像模式)。
二、鏡像模式:把需要的隊列做成鏡像隊列,存在於多個節點,屬於RabbitMQ的HA方案(鏡像模式是在普通模式的基礎上,增加一些鏡像策略)
該模式解決了上述問題,其實質和普通模式不同之處在於,消息實體會主動在鏡像節點間同步,而不是在consumer取數據時臨時拉取。該模式帶來的副作用也很明顯,除了降低系統性能外,如果鏡像隊列數量過多,加之大量的消息進入,集群內部的網絡帶寬將會被這種同步通訊大大消耗掉。所以在對可靠性要求較高的場合中適用,一個隊列想做成鏡像隊列,需要先設置policy,然后客戶端創建隊列的時候,rabbitmq集群根據“隊列名稱”自動設置是普通集群模式或鏡像隊列。具體如下:
隊列通過策略來使能鏡像。策略能在任何時刻改變,rabbitmq隊列也近可能的將隊列隨着策略變化而變化;非鏡像隊列和鏡像隊列之間是有區別的,前者缺乏額外的鏡像基礎設施,沒有任何slave,因此會運行得更快。為了使隊列稱為鏡像隊列,你將會創建一個策略來匹配隊列,設置策略有兩個鍵“ha-mode和 ha-params(可選)”。ha-params根據ha-mode設置不同的值,下面表格說明這些key的選項:
語法講解:
在cluster中任意節點啟用策略,策略會自動同步到集群節點
rabbitmqctl set_policy -p hrsystem ha-allqueue"^" '{"ha-mode":"all"}'
這行命令在vhost名稱為hrsystem創建了一個策略,策略名稱為ha-allqueue,策略模式為 all 即復制到所有節點,包含新增節點,策略正則表達式為 “^” 表示所有匹配所有隊列名稱。
例如rabbitmqctl set_policy -p hrsystem ha-allqueue "^message" '{"ha-mode":"all"}'
注意:"^message" 這個規則要根據自己修改,這個是指同步"message"開頭的隊列名稱,我們配置時使用的應用於所有隊列,所以表達式為"^"
官方set_policy說明參見
set_policy [-p vhostpath] {name} {pattern} {definition} [priority]
(http://www.rabbitmq.com/man/rabbitmqctl.1.man.html)
1、“nodes”策略和遷移master
需要注意的是設置和修改一個“nodes”策略將不會引起已經存在的master離開,盡管你讓其離開。比如:如果一個隊列在{A},並且你給它一個節點策略告知它在{B C},它將會在{A B C}。如果節點A那時失敗或者停機了,那個節點上的鏡像將不回來且隊列將繼續保持在{B C}(注:當隊列已經是鏡像隊列且同步到其它節點,就算原節點宕機,也不影響其它節點對此隊列使用)。
2、創建策略例子
隊列名稱以“ha.”開頭的隊列都是鏡像隊列,鏡像到集群內所有節點:
列名稱以“two.”開頭的隊列,其策略鏡像到集群內任何兩個節點:
隊列同步到指rabbitmq 節點 ,rabbitmqctl:
./rabbitmqctl set_policy sa-specify "^sa\.specify\." '{"ha-mode":"nodes","ha-params":["rabbit@is137","rabbit@raxtone"]}'
切記,需要把隊列同步到的節點都寫進去。
參考:
1.Rabbitmq集群高可用測試