如今互聯網應用動不動就說要HA,好像不搞個HA都很差意思說本身的應用能承載高併發,大用戶量訪問。RabbitMQ這個經典的消息組件,也必然逃不掉單點失效的尷尬局面。固然在RabbitMQ在被普遍應用於互聯網以後,就對這個HA的需求作了實現,提供了集羣供。這是RabbitMQ的內置功能,就跟普通集羣的做用同樣,就是在某個節點發生異常時,讓生產者和消費者能保持通信狀態,同時經過提供更多的節點來增長系統的吞吐量。node
RabbitMQ集羣簡介服務器
RabbitMQ的集羣實現是基於OTP分佈式通信框架實現的,該框架由Erlang語言實現。當一個節點失效的時候,客戶端會自動鏈接到集羣裏的另外一個節點,整個過程調用方不受影響,就好像沒有斷開同樣。同時當集羣承受加大的訪問量的時候,能夠經過添加更多的節點,線性的增長吞吐量和提升性能。可是RabbitMQ集羣不能保證消息不丟失(這是另一個話題了)。架構
RabbitMQ的集羣僅僅是增長節點,可是節點之間的隊列數據是不會同步的,也就是各個節點有本身獨立的隊列數據。這是RabbitMQ的默認設置。因此並非說實現了集羣,就能夠保證數據的一致,這點須要明確。併發
RabbitMQ的集羣架構簡介app
1.元數據(metadata)框架
元數據是RabbitMQ跟蹤節點、提供連接信息的基本數據信息。分佈式
這裏的元數據包含如下內容高併發
隊列元數據:隊列名稱和屬性(好比是否持久隊列或者是否自動刪除隊列)性能
Exchange元數據:Exchange名稱,類型以及屬性spa
綁定元數據(Binding):能夠理解爲一個路由表,就是如何經過Exchange找到消息隊列,將消息投到隊列。
Vhost元數據:命令空間、隊列、Exchange的權限信息以及vhost內部的綁定信息。
有了這些metadata,RabbitMQ就能跟蹤節點的所在服務器、節點之間的關係以及其餘跟蹤信息。metadata能夠保存在內存或者磁盤中。保存在磁盤的話,那麼在重啓RabbitMQ節點以後就能重建隊列和exchange。
集羣節點中的隊列
在上面介紹集羣的時候,提到了一個關鍵的點,就是隊列數據只存在於本地節點,不會同步到各個節點。在一個集羣裏面,建立一個隊列時,只會在本地節點建立隊列信息。這樣只有本地節點知道隊列的全部信息,因此一旦這個節點失效了,這個隊列和對應的綁定信息也會消失。對於消費者來講,就失去了訂閱信息,新生成的消息也沒法投遞,因爲投遞到了黑洞。
理論上看起來能夠經過metadata去恢復隊列信息,可是若是在其餘非失效節點恢復隊列信息,那麼會出現404錯誤。這樣作的目的是確保消息只存在於失效的隊列中。咱們惟一能作的就是恢復失效的節點。(以上問題只存在於支持持久的隊列,若是隊列不是持久的,能夠直接恢復,反正沒有數據須要處理)。
那麼RabbitMQ爲何不把隊列數據同步到各個節點呢?有如下2個緣由:
1.存儲空間問題
若是每一個節點都包含了隊列信息,會致使多出N倍的數據存儲空間。
2 性能問題
對應支持持久的消息隊列,保存到每一個節點中須要不少IO寫入和帶寬,從而影響整個集羣的性能。
集羣中的Exchange
整個正好跟消息隊列相反,Exchange的信息是分佈式的,也就是各個節點都具備一份完整的Exchange信息。實際上Exchange就是一個路由表,因此很容易將路由信息複製到各個節點。
(集羣中的queue和Exchange結構圖,來自《RabbitMQ In Action》
集羣節點類型
最後一部分理論,講完就進入啪啪的敲代碼實際操做。
RabbitMQ的集羣根據存儲類型能夠分紅兩種類型:內存節點和磁盤節點。這裏存儲的是metadata,內存節點保存在內存中,磁盤節點保存在磁盤中。
若是隻有一個節點(或者說是單節點),只能是磁盤節點,不然RabbitMQ重啓以後就啥也找不回了。
若是至少有一個磁盤節點,那麼其餘節點能夠定義成內存節點。存儲在內存節點的好處固然是爲了提升訪問速度。而磁盤節當然重要,也不是越多越好。由於每次變動metadata數據的時候,都須要將數據寫入到磁盤節點,那麼這個寫入會拖慢集羣的訪問效率。
因此至少保持一個磁盤節點,那麼一旦重啓或者恢復,其餘節點也能正常恢復metadata數據。
好了理論講了半天,本身都以爲囉嗦,詳細的內容仍是看文檔吧。
練習在本機搭建集羣節點
這個集羣不必定是要在多個服務器跑多個節點,一個服務器上也能夠運行多個節點,能夠理解爲多個進程監聽不一樣的端口號,而且節點的名稱不一樣。我這裏演示的是在一個服務器上運行多個節點。
僅僅是搭建集羣節點命令很簡單
1.首先確認安裝好了RabbitMQ,而後中止默認啓動的那個節點
sudo rabbitmqctl stop_app
sudo rabbitmqctl stop
2.而後依次執行如下三個命令,其實是建立了三個節點,不一樣的端口號和節點名稱
sudo RABBITMQ_NODENAME=rabbit RABBITMQ_NODE_PORT=5672 rabbitmq-server -detached
sudo RABBITMQ_NODENAME=rabbit_1 RABBITMQ_NODE_PORT=5673 rabbitmq-server -detached
sudo RABBITMQ_NODENAME=rabbit_2 RABBITMQ_NODE_PORT=5674 rabbitmq-server –detached
這裏使用了兩個環境變量,分別是RABBITMQ_NODENAME和RABBITMQ_NODE_PORT,也就是定義了節點名稱和節點的監聽端口號。
運行成功以後,就應該有三個節點在運行,可依次執行如下命令驗證
sudo rabbitmqctl -n rabbit@bogon status
sudo rabbitmqctl -n rabbit_1@bogon status
sudo rabbitmqctl -n rabbit_2@bogon status
這裏運行rabbitmqctl時指定了節點的名稱-n,格式爲:節點名@主機名, bogon是個人主機名,要根據實際狀況修改。
3.造成集羣
目前爲止有三個節點,可是這三個節點是獨立運行的,沒有任何關聯,下面來創建集羣關係。
咱們以第一個節點爲metadata的提供節點,其餘2個節點做爲集羣節點加入,也就是第2和第3個節點要獲得第一個節點的metadata信息。
首先中止節點2
sudo rabbitmqctl -n rabbit_1@bogon stop_app
重置節點2的metadata
sudo rabbitmqctl -n rabbit_1@bogon reset
將節點2加入到節點1中
sudo rabbitmqctl -n rabbit_1@bogon join_cluster rabbit@bogon
(這裏加入節點後,節點2將做爲磁盤節點)
啓動節點2
sudo rabbitmqctl -n rabbit_1@bogon start_app
查看節點2的集羣狀態
sudo rabbitmqctl -n rabbit_1@bogon cluster_status
輸出以下信息
[{nodes,[{disc,[rabbit@bogon,rabbit_1@bogon]}]},
{running_nodes,[rabbit@bogon,rabbit_1@bogon]},
能夠看到目前有兩個節點,都是磁盤類型的節點,而且都在運行
繼續加入節點3,前兩部中止和重置同樣,只是改個節點名稱
sudo rabbitmqctl -n rabbit_2@bogon stop_app
sudo rabbitmqctl -n rabbit_2@bogon reset
將節點3加入到節點1,可是設置節點3位內存節點,注意最後的--ram參數
sudo rabbitmqctl -n rabbit_2@bogon join_cluster rabbit@bogon –ram
啓動節點3並查看集羣狀態
sudo rabbitmqctl -n rabbit_2@bogon start_app
sudo rabbitmqctl -n rabbit_2@bogon cluster_status
輸出信息以下
[{nodes,[{disc,[rabbit_1@bogon,rabbit@bogon]},{ram,[rabbit_2@bogon]}]},
{running_nodes,[rabbit@bogon,rabbit_1@bogon,rabbit_2@bogon]},
能夠看到目前有三個節點,第三個節點是內存節點。
好了,到目前爲此單機多節點就搭建完了,那有什麼用呢?上述顯示的running_nodes都是能夠被鏈接的,最起碼增長了鏈接數,也有個最起碼的概念,接下來會整理如何將節點分佈運行在不一樣的服務器。