一些概念
Installing on RPM-based Linux (RedHat Enterprise Linux, CentOS, Fedora, openSUSE)
Classic Mirrored Queues
采用集群的目的
RabbitMQ本身是基於Erlang編寫,Erlang語言天生具備分布式特性(通過同步Erlang集群各節點的erlang.cookie來實現)。因此,RabbitMQ天然支持集群。集群是保證可靠性的一種方式,同時可以通過水平擴展以達到增加消息吞吐量能力的目的。
集群的配置方式
RabbitMQ可以通過三種方法來部署分布式集群系統,分別是:
- cluster:不支持跨網段,用於同一個網段內的局域網;可以隨意的動態增加或者減少;;節點之間需要運行相同版本的RabbitMQ和Erlang。
- federation:應用於廣域網,允許單台服務器上的交換機或隊列接收發布到另一台服務器上交換機或隊列的消息,可以是單獨機器或集群。federation隊列類似於單向點對點連接,消息會在聯盟隊列之間轉發任意次,直到被消費者接受。通常使用federation來連接internet上的中間服務器,用作訂閱分發消息或工作隊列。
- shovel:連接方式與federation的連接方式類似,但它工作在更低層次。可以應用於廣域網。
RabbitMQ Cluster集群同步原理
上圖是采用了三個節點的的RabbitMQ集群,Exchange A
的元數據信息在所有的節點上是一致的,而Queue的完整數據則只會存放在創建它的節點上。其他節點只存儲這個Queue的metadata信息和一個指向Queue的主節點(owner node)的指針。
RabbitMQ集群元數據的同步
RabbitMQ集群會始終同步四種類型的內部元數據(類似索引):
- 隊列元數據:隊列名稱和它的屬性。
- 交換器元數據:交換器名稱、類型和屬性。
- 綁定元數據:一張簡單的表格展示了如何將消息路由到隊列。
- vhost元數據:為vhost內的隊列、交換器和綁定提供命名空間和安全屬性;因此,當用戶訪問其中任何一個RabbitMQ節點時,通過rabbitmqctl查詢到的
queue、user、exchange、vhost
等信息都是相同的。
為何RabbitMQ集群僅采用元數據同步的方式?
- 存儲空間,如果每個集群節點都擁有所有Queue的完全數據拷貝,那么每個節點的存儲空間會非常大,集群的消息積壓能力會非常弱(無法通過集群節點的擴容提高消息積壓能力)。
- 性能,消息的發布者需要將消息復制到每一個集群節點,對於持久化消息,網絡和磁盤同步復制的開銷都會明顯增加。
RabbitMQ集群發送/訂閱消息的基本原理
場景1:客戶端直接連接隊列所在節點
如果有一個消息生產者或者消息消費者通過amqp-client的客戶端連接至節點1進行消息的發布或者訂閱,那么此時的集群中的消息收發只與節點1相關,這個沒有任何問題;如果客戶端相連的是節點2或者節點3(隊列1數據不在該節點上),那么情況又會是怎么樣呢?
場景2:客戶端連接的是非隊列數據所在節點
如果消息生產者所連接的是節點2或者節點3,此時隊列1的完整數據不在這兩個節點上,那么在發送消息過程中這兩個節點主要起了一個路由轉發作用,根據這兩個節點上的元數據(也就是上文提到的:指向queue的owner node的指針)轉發至節點1上,最終發送的消息還是會存儲至節點1的隊列1上。
同樣,如果消息消費者所連接的節點2或者節點3,那這兩個節點也會作為路由節點起到轉發作用,將會從節點1的隊列1中拉取消息進行消費。
根據上述的原理,RabbitMQ集群有兩種工作模式:
- 普通模式:當消息進入A節點的Queue中后,consumer從B節點拉取時,RabbitMQ會臨時在A、B間進行消息傳輸,把A中的消息實體取出並經過B發送給consumer,所以consumer應平均連接每一個節點,從中取消息。該模式存在一個問題就是當A節點故障后,B節點無法取到A節點中還未消費的消息實體。如果做了隊列持久化或消息持久化,那么得等A節點恢復,然后才可被消費,並且在A節點恢復之前其它節點不能再創建A節點已經創建過的持久隊列;如果沒有持久化的話,消息就會失丟。這種模式更適合非持久化隊列,只有該隊列是非持久的,客戶端才能重新連接到集群里的其他節點,並重新創建隊列。假如該隊列是持久化的,那么唯一辦法是將故障節點恢復起來。為什么RabbitMQ不將隊列復制到集群里每個節點呢?這與它的集群的設計本意相沖突,集群的設計目的就是增加更多節點時,能線性的增加性能(CPU、內存)和容量(內存、磁盤)。當然RabbitMQ新版本集群也支持隊列復制(有個選項可以配置)。比如在有五個節點的集群里,可以指定某個隊列的內容在2個節點上進行存儲,從而在性能與高可用性之間取得一個平衡(應該就是指鏡像模式)。
- 鏡像模式:其實質和普通模式不同之處在於,消息實體會主動在鏡像節點間同步,而不是在consumer取數據時臨時拉取。該模式帶來的副作用也很明顯,除了降低系統性能外,如果鏡像隊列數量過多,加之大量的消息進入,集群內部的網絡帶寬將會被這種同步通訊大大消耗掉。所以在對可靠性要求較高的場合中適用.
節點類型
- RAM Node:內存節點將所有的隊列、交換機、綁定、用戶、權限和vhost的元數據定義存儲在內存中,好處是可以使得像交換機和隊列聲明等操作更加的快速。
- Disk Node:將元數據存儲在磁盤中,單節點系統只允許磁盤類型的節點,防止重啟RabbitMQ的時候,丟失系統的配置信息。
不同的節點類型在在web管理界面中有體現:
如果是內存結點這里就顯示為RAM,磁盤節點顯示為disk。
注意:
- RabbitMQ要求在集群中至少有一個磁盤節點,所有其他節點可以是內存節點,當節點加入或者離開集群時,必須要將該變更通知到至少一個磁盤節點。
- 如果集群中唯一的一個磁盤節點崩潰的話,集群仍然可以保持運行,但是無法進行其他操作(包括創建隊列、交換器、綁定,添加用戶、更改權限、添加和刪除集群結點),直到節點恢復。
- 解決方案:設置兩個磁盤節點,至少有一個是可用的,可以保存元數據的更改。
Erlang Cookie
Erlang Cookie是保證不同節點可以相互通信的密鑰,要保證集群中的不同節點相互通信必須共享相同的Erlang Cookie。具體的目錄存放在/var/lib/rabbitmq/.erlang.cookie
。
接下來的演示基於docker部署rabbitmq集群。
普通模式集群的搭建
先來看如何搭建普通模式的集群。
這里的案例是搭建一個四個節點的RabbitMQ集群,各有兩個RAM和DISK節點。
啟動各節點容器
# 拉取鏡像
docker pull rabbitmq:management
# ------------ mq1 -------------
docker run \
-d \
--name rabbitmq1 \
--hostname mq1 \
--restart=always \
-e RABBITMQ_DEFAULT_USER=guest \
-e RABBITMQ_DEFAULT_PASS=12346 \
-v /data/rabbitmq_data/mq1:/var/lib/rabbitmq \
-p 5673:5672 \
-p 15673:15672 \
rabbitmq:management
# ------------ mq2 -------------
docker run \
-d \
--name rabbitmq2 \
--hostname mq2 \
--restart=always \
-e RABBITMQ_DEFAULT_USER=guest \
-e RABBITMQ_DEFAULT_PASS=12346 \
--link rabbitmq1:mq1 \
-v /data/rabbitmq_data/mq2:/var/lib/rabbitmq \
-p 5674:5672 \
-p 15674:15672 \
rabbitmq:management
# ------------ mq3 -------------
docker run \
-d \
--name rabbitmq3 \
--hostname mq3 \
--restart=always \
-e RABBITMQ_DEFAULT_USER=guest \
-e RABBITMQ_DEFAULT_PASS=12346 \
--link rabbitmq1:mq1 \
--link rabbitmq2:mq2 \
-v /data/rabbitmq_data/mq3:/var/lib/rabbitmq \
-p 5675:5672 \
-p 15675:15672 \
rabbitmq:management
# ------------ mq4 -------------
docker run \
-d \
--name rabbitmq4 \
--hostname mq4 \
--restart=always \
-e RABBITMQ_DEFAULT_USER=guest \
-e RABBITMQ_DEFAULT_PASS=12346 \
--link rabbitmq1:mq1 \
--link rabbitmq2:mq2 \
--link rabbitmq3:mq3 \
-v /data/rabbitmq_data/mq4:/var/lib/rabbitmq \
-p 5676:5672 \
-p 15676:15672 \
rabbitmq:management
需要注意的是:多個容器之間使用--link
進行連接,此屬性不能少。
確認各節點的.erlang.cookie文件的一致性
現在,各節點順利啟動了,但是各節點的.erlang.cookie
並不一定是一致的,如果出現各節點.erlang.cookie
不一致情況,則后續的組建集群就會失敗,所以要檢查並確認.erlang.cookie
的一致性:
# 如果是docker容器搭建的集群, .erlang.cookie 查看方式
[root@cs ~]# docker exec -it rabbitmq1 bash
root@mq1:/# cat /var/lib/rabbitmq/.erlang.cookie
ZMOLTOWMHAWHIEQMJSJP
同步各節點的.erlang.cookie
文件,你可能用到的命令:
# 將容器中指定目錄下的文件拷貝到宿主機的指定目錄中
docker cp 容器名:容器內文件路徑 宿主機目錄
[root@cs ~]# docker cp rabbitmq1:/var/lib/rabbitmq/.erlang.cookie /tmp
# 將宿主機中的文件拷貝到容器中指定目錄下
docker cp 宿主機文件路徑 容器名:容器內文件路徑
[root@cs ~]# docker cp /tmp/.erlang.cookie rabbitmq2:/var/lib/rabbitmq/.erlang.cookie
注意,同步完各節點的.erlang.cookie
后,需要重啟容器使之加載最新的.erlang.cookie
。
配置各節點加入集群
# ------------ mq1 -------------
# 配置mq1為disk節點
docker exec -it rabbitmq1 bash
rabbitmqctl stop_app
rabbitmqctl reset
rabbitmqctl start_app
exit
# ------------ mq2 -------------
# 配置mq2為disk節點
docker exec -it rabbitmq2 bash
rabbitmqctl stop_app
rabbitmqctl reset
rabbitmqctl join_cluster rabbit@mq1
rabbitmqctl start_app
exit
# ------------ mq3 -------------
# 配置mq3為RAM節點
docker exec -it rabbitmq3 bash
rabbitmqctl stop_app
rabbitmqctl reset
rabbitmqctl join_cluster --ram rabbit@mq1
rabbitmqctl start_app
exit
# ------------ mq4 -------------
# 配置mq4為RAM節點
docker exec -it rabbitmq4 bash
rabbitmqctl stop_app
rabbitmqctl reset
rabbitmqctl join_cluster --ram rabbit@mq1
rabbitmqctl start_app
exit
參數--ram表示設置為內存節點,忽略此參數默認為磁盤節點。
現在,web管理界面就有了這四個節點的信息:
鏡像隊列集群
鏡像隊列是基於普通的集群模式的,然后再添加一些策略,所以還是得先配置普通集群,然后才能設置鏡像隊列。鏡像隊列存在於多個節點。要實現鏡像模式,需要先搭建一個普通集群模式,在這個模式的基礎上再配置鏡像模式以實現高可用。
鏡像隊列的相關概念
鏡像隊列原理
默認的一個rabbitmq中的queue是在一個node上的,至於在那個node上取決於client 進行declare的時候的順序,但rabbitmq中的exchange、bindings都是需要在全部的節點上存在的,也就是rabbitmq集群天生就是支持自動同步這些信息的。而queue則是可以通過mirrored 同步到多個節點上,至於到底是幾個節點可以指定。
PS:rabbitmq集群中的queue不是沒有同步,是有同步的,只是他們同步的只是queue的元信息,在之前介紹中有說明。
每一個queue都會創建在一個master node或者多個slaves node上 ,並且當master節點丟失的時候,最老的slaves節點將會變成最新的master,當然這有一個前提那就是要求slave節點必須已經同步了master 節點的內容,如果沒有同步的話那么這個slave 節點是不可以成為master 節點的。更多細節涉及到了主節點的選舉機制了,這里不再過多介紹。
rabbitmq的集群和鏡像隊列區別
rabbitmq的集群是根據erlang的同步機制來實現的,這是erlang自帶的功能,十分強大,如果幾個rabbitmq node進行了集群后,這些node之前是可以同步元數據的,包括exchange的元數據、隊列的元數據、binding的元數據,但是需要注意的是不會包括同步queue的內容默認情況下。主要是因為如果同步的queue的內容的話,如果出現了queue很多的時候,並且每一個queue的內容都很大的時候,rabbitmq就僅僅需要忙着處理這些本node的queue還要負責處理和不同的node之前進行同步,這會造成系統很大負載,對rabbitmq的整體性能會有很多的影響,因此默認是關閉的。那么如果你非要進行隊列的內容同步,比如你的隊列吞吐量並不高,每一個隊列的內容也不大,對性能不會造成什么影響,那么就可以打開鏡像隊列,這個時候集群之前就會同步queue的內容了。
鏡像隊列其實就是原來channel只會按照bindings來把消息路由給某個隊列,現在如果你使用了鏡像隊列,那么channel就需要並行的把消息路由給master node 同時還需要把消息同步給slave node上,所以當使用鏡像隊列以后性能是一定會下降的。
鏡像隊列實現
鏡像隊列基本上就是一個特殊的BackingQueue,它內部包裹了一個普通的BackingQueue做本地消息持久化處理,在此基礎上增加了將消息和ack復制到所有鏡像的功能。所有對mirror_queue_master的操作,會通過可靠組播GM的方式同步到各slave節點。GM負責消息的廣播,mirror_queue_slave負責回調處理,而master上的回調處理是由coordinator負責完成。mirror_queue_slave中包含了普通的BackingQueue進行消息的存儲,master節點中BackingQueue包含在mirror_queue_master中由AMQQueue進行調用。
消息的發布與消費都是通過master節點完成。master節點對消息進行處理的同時將消息的處理動作通過GM廣播給所有的slave節點,slave節點的GM收到消息后,通過回調交由mirror_queue_slave進行實際的處理。
GM(Guarenteed Multicast)
GM模塊實現的一種可靠的組播通訊協議,該協議能夠保證組播消息的原子性,即保證組中活着的節點要么都收到消息要么都收不到。
它的實現大致如下:
將所有的節點形成一個循環鏈表,每個節點都會監控位於自己左右兩邊的節點,當有節點新增時,相鄰的節點保證當前廣播的消息會復制到新的節點上;當有節點失效時,相鄰的節點會接管保證本次廣播的消息會復制到所有的節點。在master節點和slave節點上的這些gm形成一個group,group(gm_group)的信息會記錄在mnesia中。不同的鏡像隊列形成不同的group。消息從master節點對於的gm發出后,順着鏈表依次傳送到所有的節點,由於所有節點組成一個循環鏈表,master節點對應的gm最終會收到自己發送的消息,這個時候master節點就知道消息已經復制到所有的slave節點了。
queue的master node和slave node
每一個queue都有一個home node也叫主隊列。所有的鏡像操作首先必須要通過這個master,然后在復制到其他的mirrors上,這樣做主要是為了保證消息的FIFO順序,也就是說所有順序執行的操作都要順序交給master,然后channel在同步給slave node,如果master上移出了隊列中的內容,那么所有的slave node也同步的移出所有的隊列內容。但是要記住consumer消費的只是 master node,並不是slave node。
可以通過如下的策略來決定 queue master 在那個節點上:
- 選擇存在主隊列 最少的的節點: min-masters
- 選擇client 聲明queue 連接的節點:client-local
- 隨機選擇:random
以上策略的配置可以通過如下幾個方法:
- 在創建queue的時候使用x-queue-master-locator來指明用上面的哪個策略
- 在配置文件中使用配置項 queue_master_locator指明使用上面的哪個策略
新節點同步機制
如果集群中新加了一個node,那么這個node就會變成備復制節點,而且備復制節點不會主動的復制master node 上的queue的內容,當然了他是會同步master node上的queue的元數據的,那么怎么實現和master node上的queue內容同步呢?這就需要隨着時間的推移一點一點的來同步了,也就是說新加入的節點只會同步后來加入到master node上的隊列內容並且隨着master node上的queue里面的內容被不斷的消費掉,很快備份節點和master node上面的內容就會一樣了。
但是考慮到萬一consumer正好發布消息到了新的node上,並且master node 又掛了會怎么樣呢?答案是會丟失消息。因此在master node 將現存的queue的內容復制到其他的新節點之前,分辨是否所有從copy和master node擁有相同的內容就很重要了。為了檢測鏡像隊列同步的狀態,可以使用如下的命令來確定:
rabbitmqctl list_queues pid slave_pids synchronised_slave_pids
[root@cs ~]# docker exec -it rabbitmq1 bash
root@mq1:/# rabbitmqctl list_queues pid slave_pids synchronised_slave_pids
Timeout: 60.0 seconds ...
Listing queues for vhost / ...
pid slave_pids synchronised_slave_pids
<rabbit@mq1.1604472735.7876.0> [<rabbit@mq3.1604475591.2393.0>] [<rabbit@mq3.1604475591.2393.0>]
<rabbit@mq1.1604472735.7872.0>
<rabbit@mq1.1604472735.8124.0> [<rabbit@mq2.1604474711.3278.0>] [<rabbit@mq2.1604474711.3278.0>]
這個命令會顯示出 slave-pids
和synchronised_slave_pids
如果兩個是一樣的時候就表示所有的隊列內容同步完成了,否則就是還沒有同步完成,這個時候master node是一定不可以刪除的。
鏡像隊列的策略介紹
這里通過使用policy
模塊在普通集群的基礎來完成鏡像隊列配置。
rabbitmqctl set_policy ha-all "^" '{"ha-mode": "all"}'
上述語句可以在集群中任意節點創建並啟用策略,策略會自動同步到集群節點,其中:
ha-all
為策略名稱"^"
表示匹配所有,"^logs"
表示匹配名稱為以logs開頭的Queue。ha-mode
為匹配類型,它的值有三種類型:all
:所有,表示為匹配集群中所有Queue。exctly
:部分,需要搭配ha-params
參數使用,該ha-params
參數的值為int類型,如ha-params:2
表示集群中任意的2個Node節點上的Queue。nodes
:指定,需要搭配ha-params
參數使用,該ha-params
參數的值為數組類型,如ha-params:["rabbit@mq1", "rabbit@mq2"]
表示集群中mq1
和mq2
2個Node節點上的Queue。
一些示例:
# 聲明一個策略名為 ha-all 的策略,匹配名稱以 "ha" 開頭的隊列,並將鏡像配置到集群中所有的節點
rabbitmqctl set_policy ha-all "^" '{"ha-mode": "all"}'
# 聲明一個策略名為 ha-two 的策略,匹配名稱以 "two." 開頭的隊列被鏡像到集群中的任意兩個節點,並實現自動同步
rabbitmqctl set_policy ha-two "^two\." '{"ha-mode":"exactly","ha-params":2,"ha-sync-mode":"automatic"}'
# 聲明一個策略名為 ha-nodes 的策略,匹配名稱以 "nodes." 開頭的隊列被鏡像到集群中的特定節點
rabbitmqctl set_policy ha-nodes "^nodes\." '{"ha-mode":"nodes","ha-params":["rabbit@nodeA", "rabbit@nodeB"]}'
其中,官網也告訴我們:
Note that mirroring to all nodes is rarely necessary and will result in unnecessary resource waste.
Consider mirroring to the majority (N/2+1) nodes with "ha-mode":"exactly" instead.
大致意思是沒必要將Queue鏡像到所有的節點,這會導致不必要的資源浪費;並建議使用"ha-mode":"exctly"
模式,將Queue鏡像到(N/2+1,即集群的半數加一)個節點上。
鏡像隊列的策略實戰
制定鏡像隊列的策略
使用下面的策略,來配置集群的鏡像隊列:
# 聲明一個策略名為 ha-two 的策略,匹配名稱以 "two." 開頭的隊列被鏡像到集群中的任意兩個節點,並實現自動同步
rabbitmqctl set_policy ha-two "^two\." '{"ha-mode":"exactly","ha-params":2,"ha-sync-mode":"automatic"}'
實施策略
后台執行策略:
[root@cs ~]# docker exec -it rabbitmq1 bash
root@mq1:/# rabbitmqctl set_policy ha-two "^two\." '{"ha-mode":"exactly","ha-params":2,"ha-sync-mode":"automatic"}'
Setting policy "ha-two" for pattern "^two\." to "{"ha-mode":"exactly","ha-params":2,"ha-sync-mode":"automatic"}" with priority "0" for vhost "/" ...
然后創建幾個隊列:
import pika
# 連接 RabbitMQ Server
credentials = pika.PlainCredentials('guest', '12346') # mq用戶名和密碼
connection = pika.BlockingConnection(
pika.ConnectionParameters(
host='192.168.10.91',
port=5673,
virtual_host='/',
credentials=credentials)
)
# 生產者通過 channel 對象與 RabbitMQ Server 打交道
channel = connection.channel()
# 聲明一個隊列,如果該隊列不存在就創建
channel.queue_declare(queue='news1')
channel.queue_declare(queue='two.logs-info')
channel.queue_declare(queue='two.logs-warning')
connection.close()
在web管理界面的Queues
選項中就可以看到效果了:
我們也可以在Admin
欄對策略進行管理:
PS:點擊策略名稱也可以刪除策略。
RabbitMQ3.8.0版本新隊列:仲裁隊列
仲裁隊列(Quorum Queues)是RabbitMQ3.8.0版本的新的隊列類型,它基於Raft共識算法實現的持久的FIFO隊列。
仲裁隊列類型是持久的鏡像隊列的一種替代方法,主要目標是解決集群中的數據安全。
由於仲裁隊列是新版本功能,其穩定性也有待考證,所以這里不在多表,詳情參考官網:Quorum Queues
相關命令
在集群的搭建過程中,我們也接觸到了一些命令,這里列出其他命令供參考。
# 查看所有隊列信息
rabbitmqctl list_queues
# 關閉應用(關閉當前啟動的節點)
rabbitmqctl stop_app
# 啟動應用,和上述關閉命令配合使用,達到清空隊列的目的
rabbitmqctl start_app
# 從管理數據庫中移除所有數據,例如配置過的用戶和虛擬宿主, 刪除所有持久化的消息(這個命令要在rabbitmqctl stop_app之后使用)
rabbitmqctl reset
# 作用和rabbitmqctl reset一樣,區別是無條件重置節點,不管當前管理數據庫狀態以及集群的配置。如果數據庫或者集群配置發生錯誤才使用這個最后的手段
rabbitmqctl force_reset
# 從集群中移除節點,首先要停止節點后才能移除
docker exec -it rabbitmq2 bash
rabbitmqctl stop_app
rabbitmqctl --node rabbit@mq2 reset
# 查看節點狀態
rabbitmqctl status
# 添加用戶
rabbitmqctl add_user username password
# 列出所有用戶
rabbitmqctl list_users
# 列出用戶權限
rabbitmqctl list_user_permissions username
# 修改密碼
rabbitmqctl change_password username newpassword
# 創建虛擬主機
rabbitmqctl add_vhost vhostpath
# 列出所有虛擬主機
rabbitmqctl list_vhosts
# 設置用戶權限
rabbitmqctl set_permissions -p vhostpath username ".*" ".*" ".*"
# 列出虛擬主機上的所有權限
rabbitmqctl list_permissions -p vhostpath
# 清除用戶權限
rabbitmqctl clear_permissions -p vhostpath username
# 清除隊列里的消息
rabbitmqctl -p vhostpath purge_queue blue
# 刪除用戶
rabbitmqctl delete_user username
# 刪除虛擬主機
rabbitmqctl delete_vhost vhostpath
參考:RabbitMQ學習筆記四:RabbitMQ命令(附疑難問題解決)
question
節點停止失敗
docker環境搭建集群
在將各個節點啟動后,需要將節點組成集群時,需要先停止,然后加如集群,在停止時遇到問題了:
報錯信息
[root@cs ~]# docker exec -it rabbitmq2 bash
root@mq2:/# rabbitmqctl stop_app
Stopping rabbit application on node rabbit@mq2 ...
Error: unable to perform an operation on node 'rabbit@mq2'. Please see diagnostics information and suggestions below.
Most common reasons for this are:
* Target node is unreachable (e.g. due to hostname resolution, TCP connection or firewall issues)
* CLI tool fails to authenticate with the server (e.g. due to CLI tool's Erlang cookie not matching that of the server)
* Target node is not running
In addition to the diagnostics info below:
* See the CLI, clustering and networking guides on https://rabbitmq.com/documentation.html to learn more
* Consult server logs on node rabbit@mq2
* If target node is configured to use long node names, don't forget to use --longnames with CLI tools
DIAGNOSTICS
===========
attempted to contact: [rabbit@mq2]
rabbit@mq2:
* connected to epmd (port 4369) on mq2
* epmd reports node 'rabbit' uses port 25672 for inter-node and CLI tool traffic
* TCP connection succeeded but Erlang distribution failed
* suggestion: check if the Erlang cookie identical for all server nodes and CLI tools
* suggestion: check if all server nodes and CLI tools use consistent hostnames when addressing each other
* suggestion: check if inter-node connections may be configured to use TLS. If so, all nodes and CLI tools must do that
* suggestion: see the CLI, clustering and networking guides on https://rabbitmq.com/documentation.html to learn more
Current node details:
* node name: 'rabbitmqcli-754-rabbit@mq2'
* effective user's home directory: /var/lib/rabbitmq
* Erlang cookie hash: wAcG6R9I7xf1cvxpsW8W9A==
我首先確認各節點的.erlang.cookie
文件是否一致:
[root@cs ~]# docker exec -it rabbitmq1 bash
root@mq1:/# cat /var/lib/rabbitmq/.erlang.cookie
ZMOLTOWMHAWHIEQMJSJProot@mq1:/# exit
exit
[root@cs ~]# docker exec -it rabbitmq2 bash
root@mq2:/# cat /var/lib/rabbitmq/.erlang.cookie
ZMOLTOWMHAWHIEQMJSJProot@mq2:/# exit
exit
發現兩個節點的.erlang.cookie
是一致的,但跟報錯中的.erlang.cookie
卻不一致,經過思考,這原因是docker run
啟動節點時,使用的是當時生成的.erlang.cookie
,而同步各節點的.erlang.cookie
是啟動節點之后的操作。這就導致雖然同步了.erlang.cookie
,但節點處於運行狀態,沒有利用上最新的.erlang.cookie
導致停止失敗。解決辦法就是重啟該節點,使之加載最新的.erlang.cookie
:
[root@cs ~]# docker restart rabbitmq2
rabbitmq2
[root@cs ~]# docker exec -it rabbitmq2 bash
root@mq2:/# rabbitmqctl stop_app
Stopping rabbit application on node rabbit@mq2 ...
that's all, see also:
python – 獲取RabbitMQ隊列中的消息數 | python監控rabbitmq的消息隊列數量 | Docker實戰:Docker安裝部署RabbitMQ | MQ - RabbitMQ - 架構及工作原理 | docker 安裝rabbitMQ | 理解 RabbitMQ Exchange | RabbitMQ系列教程(六)RabbitMQ Exchange類型之headers Exchange | 消息隊列 | python - pika模塊的使用中,如何動態的刪除一個 durable=True 的持久化隊列? | rabbitMq集群之鏡像模式 | Rabbitmq集群和鏡像隊列 | RabbitMQ集群原理介紹