一、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集群完善
可以根據這個架構圖,做一些RabbitMQ集群完善,主要是將內存節點作為負載,磁盤節點作為存儲。
更改集群節點類型:
# rabbitmqctl stop_app
# rabbitmqctl change_cluster_node_type ram
# rabbitmqctl change_cluster_node_type disc
# rabbitmqctl start_app
# rabbitmqctl cluster_status
如果出現錯誤:
Error: unable to connect to node rabbit@manager1: nodedown
解決方式:
# /sbin/service rabbitmq-server stop
# /sbin/service rabbitmq-server start
# rabbitmqctl status
如果要將節點移除集群,則在本節點上執行:
# rabbitmqctl stop_app &&
rabbitmqctl reset &&
rabbitmqctl start_app
然后在主節點執行:
# rabbitmqctl forget_cluster_node rabbit@manager3
節點加入集群命令:
# rabbitmqctl join_cluster rabbit@manager3 --ram
# rabbitmqctl join_cluster rabbit@manager3 --disc
參考資料: