一般來說,如果隻是為了學習RabbitMQ或者驗證業務工程的正确性那麼在本地環境或者測試環境上使用其單執行個體部署就可以了,但是出于MQ中間件本身的可靠性、并發性、吞吐量和消息堆積能力等問題的考慮,在生産環境上一般都會考慮使用RabbitMQ的叢集方案。
對于RabbitMQ這麼成熟的消息隊列産品來說,搭建它并不難并且也有不少童鞋寫過如何搭建RabbitMQ消息隊列叢集的博文,但可能仍然有童鞋并不了解其背後的原理,這會導緻其遇到性能問題時無法對叢集進行進一步的調優。本篇主要介紹RabbitMQ叢集方案的原理,如何搭建具備負載均衡能力的中小規模RabbitMQ叢集,并最後給出生産環境建構一個能夠具備高可用、高可靠和高吞吐量的中小規模RabbitMQ叢集設計方案。
一、RabbitMQ叢集方案的原理
RabbitMQ這款消息隊列中間件産品本身是基于Erlang編寫,Erlang語言天生具備分布式特性(通過同步Erlang叢集各節點的magic cookie來實作)。是以,RabbitMQ天然支援Clustering。這使得RabbitMQ本身不需要像ActiveMQ、Kafka那樣通過ZooKeeper分别來實作HA方案和儲存叢集的中繼資料。叢集是保證可靠性的一種方式,同時可以通過水準擴充以達到增加消息吞吐量能力的目的。 下面先來看下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叢集發送/訂閱消息的基本原理
RabbitMQ叢集的工作原理圖如下:
場景1、用戶端直接連接配接隊列所在節點
如果有一個消息生産者或者消息消費者通過amqp-client的用戶端連接配接至節點1進行消息的釋出或者訂閱,那麼此時的叢集中的消息收發隻與節點1相關,這個沒有任何問題;如果用戶端相連的是節點2或者節點3(隊列1資料不在該節點上),那麼情況又會是怎麼樣呢?
場景2、用戶端連接配接的是非隊列資料所在節點
如果消息生産者所連接配接的是節點2或者節點3,此時隊列1的完整資料不在該兩個節點上,那麼在發送消息過程中這兩個節點主要起了一個路由轉發作用,根據這兩個節點上的中繼資料(也就是上文提到的:指向queue的owner node的指針)轉發至節點1上,最終發送的消息還是會存儲至節點1的隊列1上。
同樣,如果消息消費者所連接配接的節點2或者節點3,那這兩個節點也會作為路由節點起到轉發作用,将會從節點1的隊列1中拉取消息進行消費。
二、RabbitMQ叢集的搭建
(1)搭建RabbitMQ叢集所需要安裝的元件
在搭建RabbitMQ叢集之前有必要在每台虛拟機上安裝如下的元件包,分别如下:
a.Jdk 1.8
b.Erlang運作時環境,這裡用的是otp_src_19.3.tar.gz (200MB+)
c.RabbitMq的Server元件,這裡用的rabbitmq-server-generic-unix-3.6.10.tar.gz
關于如何安裝上述三個元件的具體步驟,已經有不少博文對此進行了非常詳細的描述,那麼本文就不再贅述了。有需要的同學可以具體參考這些步驟來完成安裝。
(2)搭建10節點組成的RabbitMQ叢集
該節中主要展示的是叢集搭建,需要確定每台機器上正确安裝了上述三種元件,并且每台虛拟機上的RabbitMQ的執行個體能夠正常啟動起來。
a.編輯每台RabbitMQ的cookie檔案,以確定各個節點的cookie檔案使用的是同一個值,可以scp其中一台機器上的cookie至其他各個節點,cookie的預設路徑為/var/lib/rabbitmq/.erlang.cookie或者$HOME/.erlang.cookie,節點之間通過cookie确定互相是否可通信。
b.配置各節點的hosts檔案( vim /etc/hosts)
xxx.xxx.xxx.xxx rmq-broker-test-1
xxx.xxx.xxx.xxx rmq-broker-test-2
xxx.xxx.xxx.xxx rmq-broker-test-3
......
xxx.xxx.xxx.xxx rmq-broker-test-10
c.逐個節點啟動RabbitMQ服務
rabbitmq-server -detached
d.檢視各個節點和叢集的工作運作狀态
rabbitmqctl status, rabbitmqctl cluster_status
e.以rmq-broker-test-1為主節點,在rmq-broker-test-2上:
rabbitmqctl stop_app
rabbitmqctl reset
rabbitmqctl join_cluster rabbit@rmq-broker-test-2
rabbitmqctl start_app
在其餘的節點上的操作步驟與rmq-broker-test-2虛拟機上的一樣。
d.在RabbitMQ叢集中的節點隻有兩種類型:記憶體節點/磁盤節點,單節點系統隻運作磁盤類型的節點。而在叢集中,可以選擇配置部分節點為記憶體節點。
記憶體節點将所有的隊列,交換器,綁定關系,使用者,權限,和vhost的中繼資料資訊儲存在記憶體中。而磁盤節點将這些資訊儲存在磁盤中,但是記憶體節點的性能更高,為了保證叢集的高可用性,必須保證叢集中有兩個以上的磁盤節點,來保證當有一個磁盤節點崩潰了,叢集還能對外提供通路服務。在上面的操作中,可以通過如下的方式,設定新加入的節點為記憶體節點還是磁盤節點:
#加入時候設定節點為記憶體節點(預設加入的為磁盤節點)
[root@mq-testvm1 ~]# rabbitmqctl join_cluster rabbit@rmq-broker-test-1 --ram
#也通過下面方式修改的節點的類型
[root@mq-testvm1 ~]# rabbitmqctl changeclusternode_type disc | ram
e.最後可以通過“rabbitmqctl cluster_status”的方式來檢視叢集的狀态,上面搭建的10個節點的RabbitMQ叢集狀态(3個節點為磁盤即诶但,7個節點為記憶體節點)如下:
Cluster status of node 'rabbit@rmq-broker-test-1'
[{nodes,[{disc,['rabbit@rmq-broker-test-1','rabbit@rmq-broker-test-2',
'rabbit@rmq-broker-test-3']},
{ram,['rabbit@rmq-broker-test-9','rabbit@rmq-broker-test-8',
'rabbit@rmq-broker-test-7','rabbit@rmq-broker-test-6',
'rabbit@rmq-broker-test-5','rabbit@rmq-broker-test-4',
'rabbit@rmq-broker-test-10']}]},
{running_nodes,['rabbit@rmq-broker-test-10','rabbit@rmq-broker-test-5',
'rabbit@rmq-broker-test-9','rabbit@rmq-broker-test-2',
'rabbit@rmq-broker-test-8','rabbit@rmq-broker-test-7',
'rabbit@rmq-broker-test-6','rabbit@rmq-broker-test-3',
'rabbit@rmq-broker-test-4','rabbit@rmq-broker-test-1']},
{cluster_name,<<"rabbit@mq-testvm1">>},
{partitions,[]},
{alarms,[{'rabbit@rmq-broker-test-10',[]},
{'rabbit@rmq-broker-test-5',[]},
{'rabbit@rmq-broker-test-9',[]},
{'rabbit@rmq-broker-test-2',[]},
{'rabbit@rmq-broker-test-8',[]},
{'rabbit@rmq-broker-test-7',[]},
{'rabbit@rmq-broker-test-6',[]},
{'rabbit@rmq-broker-test-3',[]},
{'rabbit@rmq-broker-test-4',[]},
{'rabbit@rmq-broker-test-1',[]}]}]
(3)配置HAProxy
HAProxy提供高可用性、負載均衡以及基于TCP和HTTP應用的代理,支援虛拟主機,它是免費、快速并且可靠的一種解決方案。根據官方資料,其最高極限支援10G的并發。HAProxy支援從4層至7層的網絡交換,即覆寫所有的TCP協定。就是說,Haproxy 甚至還支援 Mysql 的均衡負載。為了實作RabbitMQ叢集的軟負載均衡,這裡可以選擇HAProxy。
關于HAProxy如何安裝的文章之前也有很多同學寫過,這裡就不再贅述了,有需要的同學可以參考下網上的做法。這裡主要說下安裝完HAProxy元件後的具體配置。
HAProxy使用單一配置檔案來定義所有屬性,包括從前端IP到後端伺服器。下面展示了用于7個RabbitMQ節點組成叢集的負載均衡配置(另外3個磁盤節點用于儲存叢集的配置和中繼資料,不做負載)。同時,HAProxy運作在另外一台機器上。HAProxy的具體配置如下:
#全局配置
global
#日志輸出配置,所有日志都記錄在本機,通過local0輸出
log 127.0.0.1 local0 info
#最大連接配接數
maxconn 4096
#改變目前的工作目錄
chroot /apps/svr/haproxy
#以指定的UID運作haproxy程序
uid 99
#以指定的GID運作haproxy程序
gid 99
#以守護程序方式運作haproxy #debug #quiet
daemon
#debug
#目前程序pid檔案
pidfile /apps/svr/haproxy/haproxy.pid
#預設配置
defaults
#應用全局的日志配置
log global
#預設的模式mode{tcp|http|health}
#tcp是4層,http是7層,health隻傳回OK
mode tcp
#日志類别tcplog
option tcplog
#不記錄健康檢查日志資訊
option dontlognull
#3次失敗則認為服務不可用
retries 3
#每個程序可用的最大連接配接數
maxconn 2000
#連接配接逾時
timeout connect 5s
#用戶端逾時
timeout client 120s
#服務端逾時
timeout server 120s
maxconn 2000
#連接配接逾時
timeout connect 5s
#用戶端逾時
timeout client 120s
#服務端逾時
timeout server 120s
#綁定配置
listen rabbitmq_cluster
bind 0.0.0.0:5672
#配置TCP模式
mode tcp
#權重輪詢
balance roundrobin
#RabbitMQ叢集節點配置,其中ip1~ip7為RabbitMQ叢集節點ip位址
server rmq_node1 ip1:5672 check inter 5000 rise 2 fall 3 weight 1
server rmq_node2 ip2:5672 check inter 5000 rise 2 fall 3 weight 1
server rmq_node3 ip3:5672 check inter 5000 rise 2 fall 3 weight 1
server rmq_node4 ip4:5672 check inter 5000 rise 2 fall 3 weight 1
server rmq_node5 ip5:5672 check inter 5000 rise 2 fall 3 weight 1
server rmq_node6 ip6:5672 check inter 5000 rise 2 fall 3 weight 1
server rmq_node7 ip7:5672 check inter 5000 rise 2 fall 3 weight 1
#haproxy監控頁面位址
listen monitor
bind 0.0.0.0:8100
mode http
option httplog
stats enable
stats uri /stats
stats refresh 5s
在上面的配置中“listen rabbitmq_cluster bind 0.0.0.0:5671”這裡定義了用戶端連接配接IP位址和端口号。這裡配置的負載均衡算法是roundrobin—權重輪詢。與配置RabbitMQ叢集負載均衡最為相關的是“ server rmq_node1 ip1:5672 check inter 5000 rise 2 fall 3 weight 1”這種,它辨別并且定義了後端RabbitMQ的服務。主要含義如下:
(a)“server ”部分:定義HAProxy内RabbitMQ服務的辨別;
(b)“ip1:5672”部分:辨別了後端RabbitMQ的服務位址;
(c)“check inter ”部分:表示每隔多少毫秒檢查RabbitMQ服務是否
可用;
(d)“rise ”部分:表示RabbitMQ服務在發生故障之後,需要多少次健康檢查才能被再次确認可用;
(e)“fall ”部分:表示需要經曆多少次失敗的健康檢查之後,HAProxy才會停止使用此RabbitMQ服務。
#啟用HAProxy服務
[root@mq-testvm12 conf]# haproxy -f haproxy.cfg
啟動後,即可看到如下的HAproxy的界面圖:
(4)RabbitMQ的叢集架構設計圖
經過上面的RabbitMQ10個節點叢集搭建和HAProxy軟彈性負載均衡配置後即可組建一個中小規模的RabbitMQ叢集了,然而為了能夠在實際的生産環境使用還需要根據實際的業務需求對叢集中的各個執行個體進行一些性能參數名額的監控,從性能、吞吐量和消息堆積能力等角度考慮,可以選擇Kafka來作為RabbitMQ叢集的監控隊列使用。是以,這裡先給出了一個中小規模RabbitMQ叢集架構設計圖:
對于消息的生産和消費者可以通過HAProxy的軟負載将請求分發至RabbitMQ叢集中的Node1~Node7節點,其中Node8~Node10的三個節點作為磁盤節點儲存叢集中繼資料和配置資訊。鑒于篇幅原因這裡就不在對監控部分進行詳細的描述的,會在後續篇幅中對如何使用RabbitMQ的HTTP API接口進行監控資料統計進行詳細闡述。
三、總結
本文主要詳細介紹了RabbitMQ叢集的工作原理和如何搭建一個具備負載均衡能力的中小規模RabbitMQ叢集的方法,并最後給出了RabbitMQ叢集的架構設計圖。限于筆者的才疏學淺,對本文内容可能還有了解不到位的地方,如有闡述不合理之處還望留言一起探讨。