摘要:實際生產應用中都會採用消息隊列的集羣方案,若是選擇RabbitMQ那麼有必要了解下它的集羣方案原理前端
通常來講,若是隻是爲了學習RabbitMQ或者驗證業務工程的正確性那麼在本地環境或者測試環境上使用其單實例部署就能夠了,可是出於MQ中間件自己的可靠性、併發性、吞吐量和消息堆積能力等問題的考慮,在生產環境上通常都會考慮使用RabbitMQ的集羣方案。
對於RabbitMQ這麼成熟的消息隊列產品來講,搭建它並不難而且也有很多童鞋寫過如何搭建RabbitMQ消息隊列集羣的博文,但可能仍然有童鞋並不瞭解其背後的原理,這會致使其遇到性能問題時沒法對集羣進行進一步的調優。本篇主要介紹RabbitMQ集羣方案的原理,如何搭建具有負載均衡能力的中小規模RabbitMQ集羣,並最後給出生產環境構建一個可以具有高可用、高可靠和高吞吐量的中小規模RabbitMQ集羣設計方案。node
RabbitMQ這款消息隊列中間件產品自己是基於Erlang編寫,Erlang語言天生具有分佈式特性(經過同步Erlang集羣各節點的magic cookie來實現)。所以,RabbitMQ自然支持Clustering。這使得RabbitMQ自己不須要像ActiveMQ、Kafka那樣經過ZooKeeper分別來實現HA方案和保存集羣的元數據。集羣是保證可靠性的一種方式,同時能夠經過水平擴展以達到增長消息吞吐量能力的目的。 下面先來看下RabbitMQ集羣的總體方案:算法
上面圖中採用三個節點組成了一個RabbitMQ的集羣,Exchange A(交換器,對於RabbitMQ基礎概念不太明白的童鞋能夠看下基礎概念)的元數據信息在全部節點上是一致的,而Queue(存放消息的隊列)的完整數據則只會存在於它所建立的那個節點上。,其餘節點只知道這個queue的metadata信息和一個指向queue的owner node的指針。sql
(1)RabbitMQ集羣元數據的同步vim
RabbitMQ集羣會始終同步四種類型的內部元數據(相似索引):
a.隊列元數據:隊列名稱和它的屬性;
b.交換器元數據:交換器名稱、類型和屬性;
c.綁定元數據:一張簡單的表格展現瞭如何將消息路由到隊列;
d.vhost元數據:爲vhost內的隊列、交換器和綁定提供命名空間和安全屬性;
所以,當用戶訪問其中任何一個RabbitMQ節點時,經過rabbitmqctl查詢到的queue/user/exchange/vhost等信息都是相同的。後端
(2)爲什麼RabbitMQ集羣僅採用元數據同步的方式安全
我想確定有很多同窗會問,想要實現HA方案,那將RabbitMQ集羣中的全部Queue的完整數據在全部節點上都保存一份不就能夠了麼?(能夠相似MySQL的主主模式嘛)這樣子,任何一個節點出現故障或者宕機不可用時,那麼使用者的客戶端只要能鏈接至其餘節點可以照常完成消息的發佈和訂閱嘛。
我想RabbitMQ的做者這麼設計主要仍是基於集羣自己的性能和存儲空間上來考慮。第一,存儲空間,若是每一個集羣節點都擁有全部Queue的徹底數據拷貝,那麼每一個節點的存儲空間會很是大,集羣的消息積壓能力會很是弱(沒法經過集羣節點的擴容提升消息積壓能力);第二,性能,消息的發佈者須要將消息複製到每個集羣節點,對於持久化消息,網絡和磁盤同步複製的開銷都會明顯增長。服務器
(3)RabbitMQ集羣發送/訂閱消息的基本原理cookie
RabbitMQ集羣的工做原理圖以下:網絡
場景一、客戶端直接鏈接隊列所在節點
若是有一個消息生產者或者消息消費者經過amqp-client的客戶端鏈接至節點1進行消息的發佈或者訂閱,那麼此時的集羣中的消息收發只與節點1相關,這個沒有任何問題;若是客戶端相連的是節點2或者節點3(隊列1數據不在該節點上),那麼狀況又會是怎麼樣呢?
場景二、客戶端鏈接的是非隊列數據所在節點
若是消息生產者所鏈接的是節點2或者節點3,此時隊列1的完整數據不在該兩個節點上,那麼在發送消息過程當中這兩個節點主要起了一個路由轉發做用,根據這兩個節點上的元數據(也就是上文提到的:指向queue的owner node的指針)轉發至節點1上,最終發送的消息仍是會存儲至節點1的隊列1上。
一樣,若是消息消費者所鏈接的節點2或者節點3,那這兩個節點也會做爲路由節點起到轉發做用,將會從節點1的隊列1中拉取消息進行消費。
(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 <name>」部分:定義HAProxy內RabbitMQ服務的標識;
(b)「ip1:5672」部分:標識了後端RabbitMQ的服務地址;
(c)「check inter <value>」部分:表示每隔多少毫秒檢查RabbitMQ服務是否可用;
(d)「rise <value>」部分:表示RabbitMQ服務在發生故障以後,須要多少次健康檢查才能被再次確承認用;
(e)「fall <value>」部分:表示須要經歷多少次失敗的健康檢查以後,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集羣的架構設計圖。限於筆者的才疏學淺,對本文內容可能還有理解不到位的地方,若有闡述不合理之處還望留言一塊兒探討。
歡迎你們關注我新開通的公衆號【風平浪靜如碼】,海量Java相關文章,學習資料都會在裏面更新,整理的資料也會放在裏面。(吸粉不易,多多支持)
以爲寫的還不錯的就點個贊,加個關注唄!點關注,不迷路,持續更新!!!