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等信息都是相同的。

(2)為何RabbitMQ集群僅采用元數據同步的方式

我想肯定有不少同學會問,想要實現HA方案,那將RabbitMQ集群中的所有Queue的完整數據在所有節點上都保存一份不就可以了么?(可以類似MySQL的主主模式嘛)這樣子,任何一個節點出現故障或者宕機不可用時,那么使用者的客戶端只要能連接至其他節點能夠照常完成消息的發布和訂閱嘛。
我想RabbitMQ的作者這么設計主要還是基於集群本身的性能和存儲空間上來考慮。第一,存儲空間,如果每個集群節點都擁有所有Queue的完全數據拷貝,那么每個節點的存儲空間會非常大,集群的消息積壓能力會非常弱(無法通過集群節點的擴容提高消息積壓能力);第二,性能,消息的發布者需要將消息復制到每一個集群節點,對於持久化消息,網絡和磁盤同步復制的開銷都會明顯增加。

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

場景1、客戶端直接連接隊列所在節點

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

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

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

配置集群前須知

主機名解析

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

如果要使用節點名稱的完整主機名(RabbitMQ默認為短名稱),並且可以使用DNS解析完整的主機名,則可能需要調查設置環境變量  RABBITMQ_USE_LONGNAME = true

創建集群的方法用多種

  通過配置文件

  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把rabbit2、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

  1、停止rabbir@c2的rabbitmq應用程序,

  2、加入rabbit@c1集群

  3、然后啟動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.

現在我們在rabbit1、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上:

  1、我們停止RabbitMQ應用程序,

  2、重置節點

  3、重新啟動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.

  

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM