RabbitMQ集羣

RabbitMQ集羣原理

上面圖中採用三個節點組成了一個RabbitMQ的集羣, Exchange A(交換器,對於RabbitMQ基礎概念不太明白的童鞋能夠看下基礎概念) 的元數據信息在全部節點上是一致的,而Queue(存放消息的隊列)的完整數據則只會存在於它所建立的那個節點上。,其餘節點只知道這個queue的metadata信息和一個指向queue的owner node的指針。

(1)RabbitMQ集羣元數據的同步

RabbitMQ集羣會始終同步四種類型的內部元數據(相似索引):
  a.隊列元數據:隊列名稱和它的屬性;
  b.交換器元數據:交換器名稱、類型和屬性;
  c.綁定元數據:一張簡單的表格展現瞭如何將消息路由到隊列;
  d.vhost元數據:爲vhost內的隊列、交換器和綁定提供命名空間和安全屬性;
  所以,當用戶訪問其中任何一個RabbitMQ節點時,經過rabbitmqctl查詢到的queue/user/exchange/vhost等信息都是相同的。html

(2)爲什麼RabbitMQ集羣僅採用元數據同步的方式

我想確定有很多同窗會問,想要實現HA方案,那將RabbitMQ集羣中的全部Queue的完整數據在全部節點上都保存一份不就能夠了麼?(能夠相似MySQL的主主模式嘛)這樣子,任何一個節點出現故障或者宕機不可用時,那麼使用者的客戶端只要能鏈接至其餘節點可以照常完成消息的發佈和訂閱嘛。
我想RabbitMQ的做者這麼設計主要仍是基於集羣自己的性能和存儲空間上來考慮。第一,存儲空間,若是每一個集羣節點都擁有全部Queue的徹底數據拷貝,那麼每一個節點的存儲空間會很是大,集羣的消息積壓能力會很是弱(沒法經過集羣節點的擴容提升消息積壓能力);第二,性能,消息的發佈者須要將消息複製到每個集羣節點,對於持久化消息,網絡和磁盤同步複製的開銷都會明顯增長。node

(3)RabbitMQ集羣發送/訂閱消息的基本原理

場景一、客戶端直接鏈接隊列所在節點

若是有一個消息生產者或者消息消費者經過amqp-client的客戶端鏈接至節點1進行消息的發佈或者訂閱,那麼此時的集羣中的消息收發只與節點1相關,這個沒有任何問題;若是客戶端相連的是節點2或者節點3(隊列1數據不在該節點上),那麼狀況又會是怎麼樣呢?數據庫

場景二、客戶端鏈接的是非隊列數據所在節點

若是消息生產者所鏈接的是節點2或者節點3,此時隊列1的完整數據不在該兩個節點上,那麼在發送消息過程當中這兩個節點主要起了一個路由轉發做用,根據這兩個節點上的元數據(也就是上文提到的:指向queue的owner node的指針)轉發至節點1上,最終發送的消息仍是會存儲至節點1的隊列1上。
一樣,若是消息消費者所鏈接的節點2或者節點3,那這兩個節點也會做爲路由節點起到轉發做用,將會從節點1的隊列1中拉取消息進行消費。安全

配置集羣前須知

主機名解析bash

RabbitMQ節點使用域名相互尋址,所以全部集羣成員的主機名必須可以從全部集羣節點解析,能夠修改hosts文件或者使用DNS解析cookie

若是要使用節點名稱的完整主機名(RabbitMQ默認爲短名稱),而且可使用DNS解析完整的主機名,則可能須要調查設置環境變量  RABBITMQ_USE_LONGNAME = true網絡

建立集羣的方法用多種app

  經過配置文件負載均衡

  rabbitmqctl手動配置工具

  經過插件(如:AWS(EC2)實例發現,Kubernetes發現,基於Consul的發現,基於etcd的發現)

一個集羣的組成能夠動態改變,全部的RabbitMQ開始做爲單個節點運行,這些節點能夠加入到集羣,而後也能夠再次脫離集羣轉回單節點

RabbitMQ集羣能夠容忍單個節點的故障。節點能夠隨意啓動和通知,只要它們能夠與在關閉時已知的集羣成員節點聯繫

集羣意味着在局域網使用,不建議運行跨廣域網的集羣

節點能夠是disk節點或RAM節點

RAM節點將內部數據庫表存儲在RAM中。這不包括消息,消息存儲索引,隊列索引和其餘節點狀態
在90%以上的狀況下,您但願全部節點都是磁盤節點; RAM節點是一種特殊狀況,可用於改善高排隊,交換或綁定流失的性能集羣。RAM節點不提供有意義的更高的消息速率。若有疑問,請僅使用磁盤節點
因爲RAM節點僅將內部數據庫表存儲在RAM中,所以它們必須在啓動時從對等節點同步它們。這意味着羣集必須至少包含一個磁盤節點。所以沒法手動刪除集羣中剩餘的最後一個磁盤節點

 

rabbitmqctl配置集羣

hostname ip system RabbitMQ
rabbit1 192.168.88.1 CentOS7.2.1511 3.7.0
rabbit2 192.168.88.2    
rabbit3 192.168.88.3    

綁定hosts文件

192.168.88.1 rabbit1
192.168.88.2 rabbit2
192.168.88.3 rabbit3

 

在三臺機器安裝RabbitMQ

RabbitMQ安裝教程

 

設置節點互相驗證:Erlang Cookie

RabbitMQ節點和CLI工具(例如rabbitmqctl)使用cookie來肯定它們是否被容許相互通訊,要使兩個節點可以通訊,它們必須具備相同的共享密鑰,稱爲Erlang Cookie.
Cookie只是一個字符串,最多能夠有255個字符。它一般存儲在本地文件中。該文件必須只能由全部者訪問(400權限)。每一個集羣節點必須具備相同的 cookie,文件位置/var/lib/rabbitmq/.erlang.cookie把rabbit二、rabbit3設置成和rabbit2同樣的便可,權限是400

 

正常方式啓動全部節點

rabbitmq-server -detached 
rabbitmq-server -detached 
rabbitmq-server -detached

 

如今啓動了三個獨立的RabbitMQ,咱們用cluster_status命令查看集羣狀態

[root@rabbit1 ~]# rabbitmqctl cluster_status
Cluster status of node rabbit@rabbit1 ...
[{nodes,[{disc,[rabbit@rabbit1]}]},
 {running_nodes,[rabbit@rabbit1]},
 {cluster_name,<<"rabbit@rabbit1">>},
 {partitions,[]},
 {alarms,[{rabbit@rabbit1,[]}]}]


[root@rabbit2 ~]# rabbitmqctl cluster_status
Cluster status of node rabbit@rabbit2 ...
[{nodes,[{disc,[rabbit@rabbit2]}]},
 {running_nodes,[rabbit@rabbit2]},
 {cluster_name,<<"rabbit@rabbit2">>},
 {partitions,[]},
 {alarms,[{rabbit@rabbit2,[]}]}]

 
[root@rabbit3 ~]# rabbitmqctl cluster_status
Cluster status of node rabbit@rabbit3 ...
[{nodes,[{disc,[rabbit@rabbit3]}]},
 {running_nodes,[rabbit@rabbit3]},
 {cluster_name,<<"rabbit@rabbit3">>},
 {partitions,[]},
 {alarms,[{rabbit@rabbit3,[]}]}]

 

爲了鏈接集羣中的三個節點,咱們把rabbit@c2和rabbit@c3節點加入到rabbit@c1節點集羣

首先,在rabbit@c1的簇中加入rabbit@c2

  一、中止rabbir@c2的rabbitmq應用程序,

  二、加入rabbit@c1集羣

  三、而後啓動RabbitMQ程序

注意:加入集羣會隱式重置節點,從而刪除此節點上之前存在的全部資源和數據

[root@rabbit2 ~]# rabbitmqctl stop_app
Stopping rabbit application on node rabbit@rabbit2 ...

[root@rabbit2 ~]# rabbitmqctl join_cluster rabbit@rabbit1
Clustering node rabbit@rabbit2 with rabbit@rabbit1

[root@rabbit2 ~]# rabbitmqctl start_app
Starting node rabbit@rabbit2 ...
 completed with 0 plugins.

如今咱們在rabbit一、rabbit2任意一個節點上查看集羣狀態,咱們能夠看到這兩個節點加入了一個集羣

[root@rabbit1 ~]# rabbitmqctl cluster_status
Cluster status of node rabbit@rabbit1 ...
[{nodes,[{disc,[rabbit@rabbit1,rabbit@rabbit2]}]},
 {running_nodes,[rabbit@rabbit2,rabbit@rabbit1]},
 {cluster_name,<<"rabbit@rabbit1">>},
 {partitions,[]},
 {alarms,[{rabbit@rabbit2,[]},{rabbit@rabbit1,[]}]}]

 

咱們再把rabbit3節點加入到這個集羣

[root@rabbit3 ~]# rabbitmqctl stop_app
Stopping rabbit application on node rabbit@rabbit3 ...

[root@rabbit3 ~]# rabbitmqctl join_cluster rabbit@rabbit1
Clustering node rabbit@rabbit3 with rabbit@rabbit1

[root@rabbit3 ~]# rabbitmqctl start_app
Starting node rabbit@rabbit3 ...
 completed with 0 plugins.

經過任何節點上的cluster_status命令,咱們能夠看到這三個節點加入了一個集羣

[root@rabbit1 ~]# rabbitmqctl cluster_status
Cluster status of node rabbit@rabbit1 ...
[{nodes,[{disc,[rabbit@rabbit1,rabbit@rabbit2,rabbit@rabbit3]}]},
 {running_nodes,[rabbit@rabbit3,rabbit@rabbit2,rabbit@rabbit1]},
 {cluster_name,<<"rabbit@rabbit1">>},
 {partitions,[]},
 {alarms,[{rabbit@rabbit3,[]},{rabbit@rabbit2,[]},{rabbit@rabbit1,[]}]}]

[root@rabbit2 ~]# rabbitmqctl cluster_status
Cluster status of node rabbit@rabbit2 ...
[{nodes,[{disc,[rabbit@rabbit1,rabbit@rabbit2,rabbit@rabbit3]}]},
 {running_nodes,[rabbit@rabbit3,rabbit@rabbit1,rabbit@rabbit2]},
 {cluster_name,<<"rabbit@rabbit1">>},
 {partitions,[]},
 {alarms,[{rabbit@rabbit3,[]},{rabbit@rabbit1,[]},{rabbit@rabbit2,[]}]}]

[root@rabbit3 ~]# rabbitmqctl cluster_status
Cluster status of node rabbit@rabbit3 ...
[{nodes,[{disc,[rabbit@rabbit1,rabbit@rabbit2,rabbit@rabbit3]}]},
 {running_nodes,[rabbit@rabbit1,rabbit@rabbit2,rabbit@rabbit3]},
 {cluster_name,<<"rabbit@rabbit1">>},
 {partitions,[]},
 {alarms,[{rabbit@rabbit1,[]},{rabbit@rabbit2,[]},{rabbit@rabbit3,[]}]}]

經過遵循上述步驟,咱們能夠在集羣正在運行的同時隨時向集羣添加新節點

已加入羣集的節點可隨時中止。他們也能夠崩潰。在這兩種狀況下,羣集的其他部分都會繼續運行,而且節點在再次啓動時會自動「跟上」(同步)其餘羣集節點。

 

咱們關閉rabbit@rabbit1和rabbit@rabbit3,並檢查每一步中的集羣狀態

[root@rabbit1 ~]# rabbitmqctl stop
Stopping and halting node rabbit@rabbit1 ...

[root@rabbit2 ~]# rabbitmqctl cluster_status
Cluster status of node rabbit@rabbit2 ...
[{nodes,[{disc,[rabbit@rabbit1,rabbit@rabbit2,rabbit@rabbit3]}]},
 {running_nodes,[rabbit@rabbit3,rabbit@rabbit2]},
 {cluster_name,<<"rabbit@rabbit1">>},
 {partitions,[]},
 {alarms,[{rabbit@rabbit3,[]},{rabbit@rabbit2,[]}]}]
 
[root@rabbit3 ~]# rabbitmqctl cluster_status
Cluster status of node rabbit@rabbit3 ...
[{nodes,[{disc,[rabbit@rabbit1,rabbit@rabbit2,rabbit@rabbit3]}]},
 {running_nodes,[rabbit@rabbit2,rabbit@rabbit3]},
 {cluster_name,<<"rabbit@rabbit1">>},
 {partitions,[]},
 {alarms,[{rabbit@rabbit2,[]},{rabbit@rabbit3,[]}]}]
 
 [root@rabbit3 ~]# rabbitmqctl stop
Stopping and halting node rabbit@rabbit3 ...

[root@rabbit2 ~]# rabbitmqctl cluster_status
Cluster status of node rabbit@rabbit2 ...
[{nodes,[{disc,[rabbit@rabbit1,rabbit@rabbit2,rabbit@rabbit3]}]},
 {running_nodes,[rabbit@rabbit2]},
 {cluster_name,<<"rabbit@rabbit1">>},
 {partitions,[]},
 {alarms,[{rabbit@rabbit2,[]}]}]

 

 如今咱們再次啓動節點,在咱們繼續檢查集羣狀態時

[root@rabbit3 ~]# rabbitmq-server -detached 
[root@rabbit3 ~]# rabbitmqctl cluster_status
Cluster status of node rabbit@rabbit3 ...
[{nodes,[{disc,[rabbit@rabbit1,rabbit@rabbit2,rabbit@rabbit3]}]},
 {running_nodes,[rabbit@rabbit2,rabbit@rabbit3]},
 {cluster_name,<<"rabbit@rabbit1">>},
 {partitions,[]},
 {alarms,[{rabbit@rabbit2,[]},{rabbit@rabbit3,[]}]}]

[root@rabbit2 ~]# rabbitmqctl cluster_status
Cluster status of node rabbit@rabbit2 ...
[{nodes,[{disc,[rabbit@rabbit1,rabbit@rabbit2,rabbit@rabbit3]}]},
 {running_nodes,[rabbit@rabbit3,rabbit@rabbit2]},
 {cluster_name,<<"rabbit@rabbit1">>},
 {partitions,[]},
 {alarms,[{rabbit@rabbit3,[]},{rabbit@rabbit2,[]}]}]

[root@rabbit1 ~]# rabbitmq-server -detached 
[root@rabbit1 ~]# rabbitmqctl cluster_status
Cluster status of node rabbit@rabbit1 ...
[{nodes,[{disc,[rabbit@rabbit1,rabbit@rabbit2,rabbit@rabbit3]}]},
 {running_nodes,[rabbit@rabbit2,rabbit@rabbit3,rabbit@rabbit1]},
 {cluster_name,<<"rabbit@rabbit1">>},
 {partitions,[]},
 {alarms,[{rabbit@rabbit2,[]},{rabbit@rabbit3,[]},{rabbit@rabbit1,[]}]}]

[root@rabbit2 ~]# rabbitmqctl cluster_status
Cluster status of node rabbit@rabbit2 ...
[{nodes,[{disc,[rabbit@rabbit1,rabbit@rabbit2,rabbit@rabbit3]}]},
 {running_nodes,[rabbit@rabbit1,rabbit@rabbit3,rabbit@rabbit2]},
 {cluster_name,<<"rabbit@rabbit1">>},
 {partitions,[]},
 {alarms,[{rabbit@rabbit1,[]},{rabbit@rabbit3,[]},{rabbit@rabbit2,[]}]}]

[root@rabbit3 ~]# rabbitmqctl cluster_status
Cluster status of node rabbit@rabbit3 ...
[{nodes,[{disc,[rabbit@rabbit1,rabbit@rabbit2,rabbit@rabbit3]}]},
 {running_nodes,[rabbit@rabbit1,rabbit@rabbit2,rabbit@rabbit3]},
 {cluster_name,<<"rabbit@rabbit1">>},
 {partitions,[]},
 {alarms,[{rabbit@rabbit1,[]},{rabbit@rabbit2,[]},{rabbit@rabbit3,[]}]}]

一些重要的警告:
當整個集羣關閉時,最後一個關閉的節點必須是第一個要聯機的節點。
若是要脫機的最後一個節點沒法恢復,可使用forget_cluster_node命令將其從羣集中刪除
若是全部集羣節點同時中止而且不受控制(例如斷電),則可能會留下全部節點都認爲其餘節點在其後中止的狀況。在這種狀況下,您能夠在一個節點上使用force_boot命令使其再次可引導

 

集羣移除節點

當節點再也不是節點的一部分時,須要從集羣中明確地刪除節點。咱們首先從集羣中刪除rabbit@rabbit3,並將其返回到獨立操做

在rabbit@rabbit3上:

  一、咱們中止RabbitMQ應用程序,

  二、重置節點

  三、從新啓動RabbitMQ應用程序

[root@rabbit3 ~]# rabbitmqctl stop_app
Stopping rabbit application on node rabbit@rabbit3 ...

[root@rabbit3 ~]# rabbitmqctl reset
Resetting node rabbit@rabbit3 ...

[root@rabbit3 ~]# rabbitmqctl start_app
Starting node rabbit@rabbit3 ...
 completed with 0 plugins.

 

在節點上 運行cluster_status命令確認rabbit@rabbit3如今再也不是集羣的一部分並獨立運行

[root@rabbit3 ~]# rabbitmqctl cluster_status
Cluster status of node rabbit@rabbit3 ...
[{nodes,[{disc,[rabbit@rabbit3]}]},
 {running_nodes,[rabbit@rabbit3]},
 {cluster_name,<<"rabbit@rabbit3">>},
 {partitions,[]},
 {alarms,[{rabbit@rabbit3,[]}]}]
 
[root@rabbit1 ~]# rabbitmqctl cluster_status
Cluster status of node rabbit@rabbit1 ...
[{nodes,[{disc,[rabbit@rabbit1,rabbit@rabbit2]}]},
 {running_nodes,[rabbit@rabbit2,rabbit@rabbit1]},
 {cluster_name,<<"rabbit@rabbit1">>},
 {partitions,[]},
 {alarms,[{rabbit@rabbit2,[]},{rabbit@rabbit1,[]}]}]

[root@rabbit2 ~]# rabbitmqctl cluster_status
Cluster status of node rabbit@rabbit2 ...
[{nodes,[{disc,[rabbit@rabbit1,rabbit@rabbit2]}]},
 {running_nodes,[rabbit@rabbit1,rabbit@rabbit2]},
 {cluster_name,<<"rabbit@rabbit1">>},
 {partitions,[]},
 {alarms,[{rabbit@rabbit1,[]},{rabbit@rabbit2,[]}]}]

 

咱們也能夠遠程刪除節點,例如,在處理無響應的節點時,這頗有用
好比:咱們在節點rabbit@rabbit2上把rabbit@rabbit1從集羣中移除

[root@rabbit1 ~]# rabbitmqctl stop_app
Stopping rabbit application on node rabbit@rabbit1 ...

[root@rabbit2 ~]# rabbitmqctl forget_cluster_node rabbit@rabbit1
Removing node rabbit@rabbit1 from the cluster

請注意,rabbit1仍然認爲它與rabbit2集羣 ,並試圖啓動它將致使錯誤。咱們須要從新設置才能從新啓動。

[root@rabbit1 ~]# rabbitmqctl reset     #必需要重置
Resetting node rabbit@rabbit1 ...

[root@rabbit1 ~]# rabbitmqctl start_app
Starting node rabbit@rabbit1 ...
 completed with 0 plugins.

 

如今查看集羣狀態,三個節點都時做爲獨立的節點

請注意,rabbit@rabbit2保留了簇的剩餘狀態,而rabbit@rabbit1 和rabbit@rabbit3是剛剛初始化的RabbitMQ。若是咱們想從新初始化rabbit@rabbit2,咱們按照與其餘節點相同的步驟進行:

[root@rabbit2 ~]# rabbitmqctl stop_app
Stopping rabbit application on node rabbit@rabbit2 ...

[root@rabbit2 ~]# rabbitmqctl reset
Resetting node rabbit@rabbit2 ...

[root@rabbit2 ~]# rabbitmqctl start_app
Starting node rabbit@rabbit2 ...
 completed with 0 plugins.

 

主機名更改

RabbitMQ節點使用主機名相互通訊。所以,全部節點名稱必須可以解析全部集羣對等的名稱。像rabbitmqctl這樣的工具也是如此
除此以外,默認狀況下RabbitMQ使用系統的當前主機名來命名數據庫目錄。若是主機名更改,則會建立一個新的空數據庫。爲了不數據丟失,創建一個固定和可解析的主機名相當重要。每當主機名更改時,您應該從新啓動RabbitMQ
若是要使用節點名稱的完整主機名(RabbitMQ默認爲短名稱),而且可使用DNS解析完整的主機名,則可能須要調查設置環境變量 RABBITMQ_USE_LONGNAME = true

 

從客戶端鏈接到羣集

客戶端能夠正常鏈接到羣集中的任何節點。若是該節點出現故障,而且集羣的其他部分仍然存在,那麼客戶端應該注意到已關閉的鏈接,而且應該可以從新鏈接到羣集的一些倖存的成員。一般,將節點主機名或IP地址燒入客戶端應用程序是不可取的:這會引入不靈活性,而且若是集羣配置發生更改或集羣中節點數發生更改,則須要編輯,從新編譯和從新部署客戶端應用程序。相反,咱們推薦一個更抽象的方法:這多是一個動態的DNS服務,它具備很是短的TTL配置,或者一個普通的TCP負載均衡器,或者用起搏器或相似技術實現的某種移動IP。通常來講

 

具備RAM節點的集羣

RAM節點只將其元數據保存在內存中。因爲RAM節點沒必要像光盤節點那樣寫入光盤,它們能夠更好地執行。可是請注意,因爲永久隊列數據老是存儲在磁盤上,所以性能改進將僅影響資源管理(例如添加/刪除隊列,交換或虛擬主機),但不會影響發佈速度或消耗速度
RAM節點是高級用例; 設置你的第一個羣集時,你應該不使用它們。您應該有足夠的光盤節點來處理您的冗餘要求,而後在須要時添加額外的RAM節點進行縮放
只包含RAM節點的集羣是脆弱的; 若是羣集中止,您將沒法再次啓動, 並將丟失全部數據。RabbitMQ將阻止在許多狀況下建立RAM節點的羣集,可是它不能徹底阻止它

這裏的例子僅僅爲了簡單起見,顯示了具備一個光盤和一個RAM節點的集羣; 這樣的集羣是一個糟糕的設計選擇

建立RAM節點
咱們能夠在首次加入集羣時將節點聲明爲RAM節點。像以前同樣,咱們使用rabbitmqctl join_cluster來完成此 操做,但傳遞 --ram標誌

rabbit2$ rabbitmqctl stop_app
Stopping node rabbit@rabbit2 ...done
.
rabbit2$ rabbitmqctl join_cluster --ram rabbit@rabbit1
Clustering node rabbit@rabbit2 with [rabbit@rabbit1] ...done.
rabbit2$ rabbitmqctl start_app
Starting node rabbit@rabbit2 ...done.

RAM節點在集羣狀態中顯示爲:
rabbit1$ rabbitmqctl cluster_status
Cluster status of node rabbit@rabbit1 ...
[{nodes,[{disc,[rabbit@rabbit1]},{ram,[rabbit@rabbit2]}]},
 {running_nodes,[rabbit@rabbit2,rabbit@rabbit1]}]
...done.

rabbit2$ rabbitmqctl cluster_status
Cluster status of node rabbit@rabbit2 ...
[{nodes,[{disc,[rabbit@rabbit1]},{ram,[rabbit@rabbit2]}]},
 {running_nodes,[rabbit@rabbit1,rabbit@rabbit2]}]
...done.

更改節點類型
咱們能夠將節點的類型從ram更改成disc,反之亦然。假設咱們想要顛倒rabbit @ rabbit2和rabbit @ rabbit1的類型 ,將前者從ram節點轉換爲disc節點,將後者從disc節點轉換爲ram節點。要作到這一點,咱們可使用 change_cluster_node_type命令。該節點必須先中止

rabbit2$ rabbitmqctl stop_app
Stopping node rabbit@rabbit2 ...done.

rabbit2$ rabbitmqctl change_cluster_node_type disc
Turning rabbit@rabbit2 into a disc node ...
...done.
Starting node rabbit@rabbit2 ...done.

rabbit1$ rabbitmqctl stop_app
Stopping node rabbit@rabbit1 ...done.

rabbit1$ rabbitmqctl change_cluster_node_type ram
Turning rabbit@rabbit1 into a ram node ...

rabbit1$ rabbitmqctl start_app
Starting node rabbit@rabbit1 ...done.
相關文章
相關標籤/搜索