RabbitMQ學習筆記:使用Docker部署RabbitMQ集羣

RabbitMQ本身是基於Erlang編寫,Erlang語言天生具備分佈式、高併發的特性(通過同步Erlang集羣各節點的magic cookie來實現)。

因此,RabbitMQ天生支持Clustering。這使得RabbitMQ本身不需要像ActiveMQ、Kafka那樣通過Zookeeper分別來實現HA方案和保存集羣的元數據。集羣是保證可靠性的一種方式,同時可以通過水平擴展以達到增加消息吞吐量的能力。

HA是High Available縮寫,是雙機集羣系統簡稱,指高可用性集羣,是保證業務連續性的有效解決方案,一般有兩個或兩個以上的節點,且分爲活動節點及備用節點。

三個節點架構圖

上圖中採用三個節點組成了一個RabbitMQ集羣,Exchange A(交換器)的元數據信息在所有的節點上是一致的,而Queue的完整數據則只會存在於它所創建的那個節點上,其它節點只知道這個queue的metadata信息和一個指向queue的owner node的指針。

RabbitMQ集羣元數據的同步

RabbitMQ集羣會始終同步四種類型的內部元數據(類似索引):

  • 隊列元數據:隊列名稱和它的屬性;
  • 交換器元數據:交換器名稱、類型和屬性;
  • 綁定元數據:一張簡單的表格展示瞭如何將消息路由到隊列;
  • vhost元數據:爲vhost內的隊列、交換器和綁定提供命名空間和安全屬性;

因此,當用戶訪問其中任何一個RabbitMQ節點時,通過rabbitmqctl查詢到的queue、user、exchange、vhost等信息都是相同的。

爲何RabbitMQ集羣僅採用元數據同步的方式

我想肯定會有不少同學會問,要想實現HA方案,那將RabbitMQ集羣中的所有Queue的完整數據在所有節點上都保存一份不就可以了麼?(可以類似MySQL的主從模式),這樣子,任何一個節點出現故障或者跌機不可用時,那麼使用者的客戶端只要能連接至其他節點能夠照常完成消息的發佈和訂閱。

我想RabbitMQ的作者這麼設計主要還是基於集羣本身的性能和存儲空間上來考慮。

第一,存儲空間,如果每個集羣節點都擁有所有Queue的完全數據拷貝,那麼每個節點的存儲空間會非常大,集羣的消息積壓能力會非常弱(無法通過集羣節點的擴容提高消息積壓能力)

第二,性能,消息的發佈者需要將消息複製到每一個集羣節點,對於持久化消息,網絡和磁盤同步複製的開銷都會明顯的增加。

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

RabbitMQ集羣的工作原理如下圖:

在這裏插入圖片描述

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

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

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

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

同樣,如果消息消費者所連接的節點2或者節點3,那這兩個節點也會作爲路由節點起到轉發作用,將會從節點1的隊列1中拉取消息進行消費。

磁盤節點和RAM節點

一個節點可以是一個磁盤(disk)節點或者一個RAM節點(注意:磁盤和光盤可以互換使用)。RAM節點僅將內部數據庫表存儲在RAM中,這不包括消息,消息存儲索引,隊列索引和其它節點狀態。

在大多數情況下你希望所有的節點都是磁盤(disk)節點,RAM節點是一種特殊情況,可用於提高隊列、交換器或綁定交換頻率比較高的性能。RAM節點不提供更高的發佈/消費消息速率,如有疑問,請僅使用磁盤節點。

由於RAM節點僅將內部數據庫表存儲在RAM中,因此他們必須在啓動時從對等節點同步它們。這意味着一個集羣必須至少包含一個磁盤節點。因此,不可能手動刪除集羣中最後剩餘的磁盤節點。

1.下載rabbitmq鏡像
docker pull rabbitmq:3.8-management

注意:使用後綴爲“-management”的鏡像版本,是包含網頁控制檯的。

2.使用docker images命令查看下載的鏡像
C:\Users\Administrator>docker images
REPOSITORY          TAG                 IMAGE ID            CREATED             SIZE
rabbitmq            3.8-management      5788d93cd8ad        14 hours ago        180 MB
centos              centos7             67fa590cfc1c        2 months ago        202 MB
3.啓動三個相互獨立的RabbitMQ服務
docker run -d --hostname localhost --name myrabbit1 -p 15672:15672 -p 5672:5672 rabbitmq:3.8-management
docker run -d --hostname localhost --name myrabbit2 -p 15673:15672 -p 5673:5672 rabbitmq:3.8-management
docker run -d --hostname localhost --name myrabbit3 -p 15674:15672 -p 5674:5672 rabbitmq:3.8-management

這樣我們就可以通過:http://ip:15672、http://ip:15673、http://ip:15674來訪問各個單例服務;

4.RabbitMQ集羣安裝

首先刪除上一步驟中生成的三個RabbitMQ鏡像集羣,然後再操作如下指令,生成有三個節點的集羣

docker run -d --hostname rabbit1 --name myrabbit1 -p 15672:15672 -p 5672:5672 -e RABBITMQ_ERLANG_COOKIE='rabbitcookie' rabbitmq:3.8-management
    
docker run -d --hostname rabbit2 --name myrabbit2 -p 15673:15672 -p 5673:5672 --link myrabbit1:rabbit1 -e RABBITMQ_ERLANG_COOKIE='rabbitcookie' rabbitmq:3.8-management
    
docker run -d --hostname rabbit3 --name myrabbit3 -p 15674:15672 -p 5674:5672 --link myrabbit1:rabbit1 --link myrabbit2:rabbit2 -e RABBITMQ_ERLANG_COOKIE='rabbitcookie' rabbitmq:3.8-management
  • -d: 後臺進程運行
  • hostname: RabbitMQ主機名稱
  • name:容器名稱
  • -p 15672:15672 訪問HTTP API客戶端,容器對外的接口和內部接口的映射
  • -p 5672:5672 由不帶TLS和帶TLS的AMQP 0-9-1和1.0客戶端使用

注意點:

  1. 多個容器之間使用“–link”連接,此屬性不能少;
  2. Erlang Cookie值必須相同,也就是RABBITMQ_ERLANG_COOKIE參數的值必須相同,因爲RabbitMQ是用Erlang實現的,Erlang Cookie相當於不同節點之間相互通訊的祕鑰,Erlang節點通過交換Erlang Cookie獲得認證。

掛載配置文件創建集羣方式:

docker run -d --hostname rabbit1 --name rabbitmq1 -p 15672:15672 -p 5672:5672 -v D:/rabbitmq1/conf:/etc/rabbitmq -v D:/rabbitmq1/log:/var/log/rabbitmq/log -e RABBITMQ_ERLANG_COOKIE='rabbitcookie' rabbitmq:3.8.0-management
    
docker run -d --hostname rabbit2 --name rabbitmq2 -p 15673:15672 -p 5673:5672 -v D:/rabbitmq2/conf:/etc/rabbitmq -v D:/rabbitmq2/log:/var/log/rabbitmq/log --link rabbitmq1:rabbit1 -e RABBITMQ_ERLANG_COOKIE='rabbitcookie' rabbitmq:3.8.0-management
    
docker run -d --hostname rabbit3 --name rabbitmq3 -p 15674:15672 -p 5674:5672 -v D:/rabbitmq3/conf:/etc/rabbitmq -v D:/rabbitmq3/log:/var/log/rabbitmq/log --link rabbitmq1:rabbit1 --link rabbitmq2:rabbit2 -e RABBITMQ_ERLANG_COOKIE='rabbitcookie' rabbitmq:3.8.0-management

docker run -d --hostname rabbit4 --name rabbitmq4 -p 15675:15672 -p 5675:5672 -v D:/rabbitmq4/conf:/etc/rabbitmq -v D:/rabbitmq4/log:/var/log/rabbitmq/log --link rabbitmq1:rabbit1 --link rabbitmq2:rabbit2 --link rabbitmq3:rabbit3 -e RABBITMQ_ERLANG_COOKIE='rabbitcookie' rabbitmq:3.8.0-management
4.將RabbitMQ節點加入集羣

設置節點1:

docker exec -it myrabbit1 bash
rabbitmqctl stop_app
rabbitmqctl reset
rabbitmqctl start_app
exit

設置節點2,加入到集羣:

docker exec -it myrabbit2 bash
rabbitmqctl stop_app
rabbitmqctl reset
rabbitmqctl join_cluster --ram rabbit@rabbit1
rabbitmqctl start_app
exit

參數“–ram”表示設置爲內存節點,忽略此參數默認爲磁盤節點。

設置節點3,加入集羣:

docker exec -it myrabbit3 bash
rabbitmqctl stop_app
rabbitmqctl reset
rabbitmqctl join_cluster --ram rabbit@rabbit1
rabbitmqctl start_app
exit

設置好之後,使用http:物理機IP:15672,默認賬號密碼是:guest/guest。

5.RabbitMQ集羣常用命令

查看集羣的狀態

rabbitmqctl cluster_status

停止節點

rabbitmqctl stop_app

啓動節點

rabbitmqctl start_app

重置節點

rabbitmqctl reset

6.從集羣中刪除節點
#停止節點
rabbitmqctl stop_app
#重置節點
rabbitmqctl reset
#啓動節點
rabbitmqctl start_app

7.我們可以遠程刪除節點,例如,在必須處理無響應的節點時,這很有用,例如可以刪除rabbit@rabbit1節點從rabbit@rabbit2節點
# on rabbit1
rabbitmqctl stop_app
# on rabbit2
rabbitmqctl forget_cluster_node rabbit@rabbit1

注意:此時rabbit1仍然認爲和rabbit2是在同一個集羣,並且試圖啓動它將會報錯,我們將會重置之後再重啓。

# on rabbit1
rabbitmqctl start_app
# => Starting node rabbit@rabbit1 ...
# => Error: inconsistent_cluster: Node rabbit@rabbit1 thinks it's clustered with node rabbit@rabbit2, but rabbit@rabbit2 disagrees

rabbitmqctl reset
# => Resetting node rabbit@rabbit1 ...done.

rabbitmqctl start_app
# => Starting node rabbit@rabbit1 ...
# => ...done.

8.集羣節點重置(reset)

有時可能需要重置節點(擦除其所有數據),然後使其重新加入集羣。一般來說,有兩種可能情況:節點正在運行時,以及節點由於諸如 ERL-430之 類的問題而無法啓動或無法響應CLI工具命令時。

重置節點將刪除其所有數據,集羣成員信息,已配置的運行時參數,用戶,虛擬主機以及任何其它節點數據。它還將從該羣集中永久刪除該節點。

要重置一個正在運行的響應節點,請首先使用rabbitmqctl stop_app停止RabbitMQ,然後使用rabbitmqctl reset對其進行重置:

# on rabbit1
rabbitmqctl stop_app
# => Stopping node rabbit@rabbit1 ...done.
rabbitmqctl reset
# => Resetting node rabbit@rabbit1 ...done.

對於無響應的節點,必須先使用任何必要的方法將其停止。對於無法啓動的節點,情況也是如此。

已重置並重新加入其原始集羣的節點將同步所有虛擬主機,用戶,權限和拓撲(隊列,交換,綁定),運行時參數和策略。如果選擇託管副本,他可能會同步鏡像隊列的內容。重置節點上的非鏡像隊列內容將丟失。

9.更改節點的類型disk|ram
# on rabbit3
rabbitmqctl stop_app
# => Stopping node rabbit@rabbit3 ...done.

rabbitmqctl change_cluster_node_type ram
# => Turning rabbit@rabbit3 into a ram node ...done.

rabbitmqctl start_app
# => Starting node rabbit@rabbit3 ...done.

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章