這篇文章將為大家詳細(xì)講解有關(guān)RabbitMQ集群高可用原理及實(shí)戰(zhàn)部署是怎樣的,文章內(nèi)容質(zhì)量較高,因此小編分享給大家做個(gè)參考,希望大家閱讀完這篇文章后對(duì)相關(guān)知識(shí)有一定的了解。
成都創(chuàng)新互聯(lián)公司專注于企業(yè)全網(wǎng)整合營銷推廣、網(wǎng)站重做改版、長順網(wǎng)站定制設(shè)計(jì)、自適應(yīng)品牌網(wǎng)站建設(shè)、H5場景定制、商城系統(tǒng)網(wǎng)站開發(fā)、集團(tuán)公司官網(wǎng)建設(shè)、成都外貿(mào)網(wǎng)站制作、高端網(wǎng)站制作、響應(yīng)式網(wǎng)頁設(shè)計(jì)等建站業(yè)務(wù),價(jià)格優(yōu)惠性價(jià)比高,為長順等各大城市提供網(wǎng)站開發(fā)制作服務(wù)。
今天我們一起來聊聊關(guān)于 RabbitMQ 集群原理和部署流程
在前幾篇文章中,我們?cè)敿?xì)的介紹了 RabbitMQ 的內(nèi)部結(jié)構(gòu)和使用,以及 SpringBoot 和 RabbitMQ 整合,都是基于單臺(tái) RabbitMQ 進(jìn)行使用的。
我們知道在微服務(wù)流行的當(dāng)下,一旦單臺(tái)服務(wù)器掛了,基本上就無法提供高可用的服務(wù)了,因此為了保證服務(wù)高可用,在生產(chǎn)環(huán)境上我們通常的做法是搭建一個(gè) RabbitMQ 集群,即使某臺(tái) RabbitMQ 故障了,其他正常的 RabbitMQ 服務(wù)器依然可以使用,應(yīng)用程序的持續(xù)運(yùn)行不會(huì)受到影響。
在前幾篇文章中,我們有介紹到 RabbitMQ 內(nèi)部有各種基礎(chǔ)構(gòu)件,包括隊(duì)列、交換器、綁定、虛擬主機(jī)等,他們組成了 AMQP 協(xié)議消息通信的基礎(chǔ),而這些構(gòu)件以元數(shù)據(jù)的形式存在,它始終記錄在 RabbitMQ 內(nèi)部,它們分別是:
這些元數(shù)據(jù),其實(shí)本質(zhì)是一張查詢表,里面包括了交換器名稱和一個(gè)隊(duì)列的綁定列表,當(dāng)你將消息發(fā)布到交換器中,實(shí)際上是將你所在的信道將消息上的路由鍵與交換器的綁定列表進(jìn)行匹配,然后將消息路由出去。
有了這個(gè)機(jī)制,那么在所有節(jié)點(diǎn)上傳遞交換器消息將簡單很多,而 RabbitMQ 所做的事情就是把交換器元數(shù)據(jù)拷貝到所有節(jié)點(diǎn)上,因此每個(gè)節(jié)點(diǎn)上的每條信道都可以訪問完整的交換器。
如果消息生產(chǎn)者所連接的是節(jié)點(diǎn) 2 或者節(jié)點(diǎn) 3,此時(shí)隊(duì)列1的完整數(shù)據(jù)不在該兩個(gè)節(jié)點(diǎn)上,那么在發(fā)送消息過程中這兩個(gè)節(jié)點(diǎn)主要起了一個(gè)路由轉(zhuǎn)發(fā)作用,根據(jù)這兩個(gè)節(jié)點(diǎn)上的元數(shù)據(jù)轉(zhuǎn)發(fā)至節(jié)點(diǎn)1上,最終發(fā)送的消息還是會(huì)存儲(chǔ)至節(jié)點(diǎn)1的隊(duì)列1上。
同樣,如果消息消費(fèi)者所連接的節(jié)點(diǎn)2或者節(jié)點(diǎn)3,那這兩個(gè)節(jié)點(diǎn)也會(huì)作為路由節(jié)點(diǎn)起到轉(zhuǎn)發(fā)作用,將會(huì)從節(jié)點(diǎn)1的隊(duì)列1中拉取消息進(jìn)行消費(fèi)。
與常見的集群主從架構(gòu)模式不同的地方在于:RabbitMQ 集群模式下,僅僅只是同步元數(shù)據(jù),每個(gè)隊(duì)列內(nèi)容還是在自己的服務(wù)器節(jié)點(diǎn)上。
這么設(shè)計(jì)主要還是基于集群本身的性能和存儲(chǔ)空間上來考慮:
既然每個(gè)隊(duì)列內(nèi)容還是在自己的服務(wù)器節(jié)點(diǎn)上,同樣也會(huì)帶來新的問題,那就是如果隊(duì)列所在服務(wù)器掛了,那存在服務(wù)器上的隊(duì)列數(shù)據(jù)是不是全部都丟失了?
在單個(gè)節(jié)點(diǎn)上,RabbitMQ 存儲(chǔ)數(shù)據(jù)有兩種方案:
在集群中的每個(gè)節(jié)點(diǎn),要么是內(nèi)存節(jié)點(diǎn),要么是磁盤節(jié)點(diǎn),如果是內(nèi)存節(jié)點(diǎn),會(huì)將所有的元數(shù)據(jù)信息僅存儲(chǔ)到內(nèi)存中,而磁盤節(jié)點(diǎn)則不僅會(huì)將所有元數(shù)據(jù)存儲(chǔ)到內(nèi)存上, 還會(huì)將其持久化到磁盤。
在單節(jié)點(diǎn) RabbitMQ 上,僅允許該節(jié)點(diǎn)是磁盤節(jié)點(diǎn),這樣確保了節(jié)點(diǎn)發(fā)生故障或重啟節(jié)點(diǎn)之后,所有關(guān)于系統(tǒng)的配置與元數(shù)據(jù)信息都會(huì)從磁盤上恢復(fù)。
而在 RabbitMQ 集群上,至少有一個(gè)磁盤節(jié)點(diǎn),也就是在集群環(huán)境中需要添加 2 臺(tái)及以上的磁盤節(jié)點(diǎn),這樣其中一臺(tái)發(fā)生故障了,集群仍然可以保持運(yùn)行。其它節(jié)點(diǎn)均設(shè)置為內(nèi)存節(jié)點(diǎn),這樣會(huì)讓隊(duì)列和交換器聲明之類的操作會(huì)更加快速,元數(shù)據(jù)同步也會(huì)更加高效。
為了和生產(chǎn)環(huán)境保持一致,我們選用CentOS7
操作系統(tǒng)進(jìn)行環(huán)境部署,分別創(chuàng)建 3 臺(tái)虛擬機(jī)。
# 3臺(tái)服務(wù)器的IP
197.168.24.206
197.168.24.233
197.168.24.234
放開防火墻限制,保證 3 臺(tái)服務(wù)器網(wǎng)絡(luò)都可以互通!
由于 RabbitMQ 集群連接是通過主機(jī)名來連接服務(wù)的,必須保證各個(gè)主機(jī)名之間可以 ping 通,重新設(shè)置 3 臺(tái)服務(wù)器主機(jī)名,所以需要做以下操作:
# 修改節(jié)點(diǎn)1的主機(jī)名
hostname node1
# 修改節(jié)點(diǎn)2的主機(jī)名
hostname node2
# 修改節(jié)點(diǎn)3的主機(jī)名
hostname node3
編輯/etc/hosts
文件,添加到在三臺(tái)機(jī)器的/etc/hosts
中以下內(nèi)容:
sudo vim /etc/hosts
添加內(nèi)容如下:
197.168.24.206 node1
197.168.24.233 node2
197.168.24.234 node3
RabbitMQ 基于 erlang 進(jìn)行通信,相比其它的軟件,安裝有些麻煩,不過本例采用rpm
方式安裝,任何新手都可以完成安裝,過程如下!
輸入如下命令,完成安裝前的環(huán)境準(zhǔn)備。
yum install lsof build-essential openssl openssl-devel unixODBC unixODBC-devel make gcc gcc-c++ kernel-devel m4 ncurses-devel tk tc xz wget vim
本次下載的是RabbitMQ-3.6.5
版本,采用rpm
一鍵安裝,適合新手直接上手。
先創(chuàng)建一個(gè)rabbitmq
目錄,本例的目錄路徑為/usr/app/rabbitmq
,然后在目錄下執(zhí)行如下命令,下載安裝包!
wget www.rabbitmq.com/releases/erlang/erlang-18.3-1.el7.centos.x86_64.rpm
wget http://repo.iotti.biz/CentOS/7/x86_64/socat-1.7.3.2-5.el7.lux.x86_64.rpm
wget www.rabbitmq.com/releases/rabbitmq-server/v3.6.5/rabbitmq-server-3.6.5-1.noarch.rpm
最終目錄文件如下:
下載完之后,按順序依次安裝軟件包,這個(gè)很重要哦~
rpm -ivh erlang-18.3-1.el7.centos.x86_64.rpm
rpm -ivh socat-1.7.3.2-5.el7.lux.x86_64.rpm
rpm -ivh rabbitmq-server-3.6.5-1.noarch.rpm
安裝完成之后,修改rabbitmq
的配置,默認(rèn)配置文件在/usr/lib/rabbitmq/lib/rabbitmq_server-3.6.5/ebin
目錄下。
vim /usr/lib/rabbitmq/lib/rabbitmq_server-3.6.5/ebin/rabbit.app
修改loopback_users
節(jié)點(diǎn)的值!
分別重新命令rabbit
節(jié)點(diǎn)名稱
vim /etc/rabbitmq/rabbitmq-env.conf
在文件里添加一行,如下配置!
NODENAME=rabbit@node1
其它兩個(gè)節(jié)點(diǎn)命令也類似,然后,再保存!通過如下命令,啟動(dòng)服務(wù)即可!
# 啟動(dòng)服務(wù)
rabbitmq-server start &
# 停止服務(wù)
rabbitmqctl stop
通過如下命令,查詢服務(wù)是否啟動(dòng)成功!
lsof -i:5672
如果出現(xiàn)5672
已經(jīng)被監(jiān)聽,說明已經(jīng)啟動(dòng)成功!
輸入如下命令,啟動(dòng)控制臺(tái)!
rabbitmq-plugins enable rabbitmq_management
用瀏覽器打開http://ip:15672
,這里的ip
就是 CentOS 系統(tǒng)的 ip,結(jié)果如下:
賬號(hào)、密碼,默認(rèn)為guest
,如果出現(xiàn)無法訪問,檢測防火墻是否開啟,如果開啟將其關(guān)閉即可!
登錄之后的監(jiān)控平臺(tái),界面如下:
RabbitMQ 集群環(huán)境下,元數(shù)據(jù)同步基于 cookie 共享方案實(shí)現(xiàn)。
在這里將 node1 的 cookie 文件復(fù)制到 node2,由于這個(gè)文件權(quán)限是 400 為方便傳輸,先修改權(quán)限,非必須操作,所以需要先修改 node1 中的該文件權(quán)限為 777
chmod 777 /var/lib/rabbitmq/.erlang.cookie
用 scp 拷貝到節(jié)點(diǎn) 2,節(jié)點(diǎn) 3 的操作也類似。
scp /var/lib/rabbitmq/.erlang.cookie node2:/var/lib/rabbitmq/
最后,將權(quán)限改回來
chmod 400 /var/lib/rabbitmq/.erlang.cookie
在節(jié)點(diǎn) 2 執(zhí)行如下命令:
# 停止rabbitmq服務(wù)
rabbitmqctl stop_app
# 清空節(jié)點(diǎn)狀態(tài)
rabbitmqctl reset
# node2和node1構(gòu)成集群,node2必須能通過node1的主機(jī)名ping通
rabbitmqctl join_cluster rabbit@node1
# 開啟rabbitmq服務(wù)
rabbitmqctl start_app
節(jié)點(diǎn) 3 的操作也類似!
在任意一臺(tái)機(jī)上面查看集群狀態(tài):
rabbitmqctl cluster_status
登錄可視化管控臺(tái),可以很清晰的看到,三個(gè)服務(wù)節(jié)點(diǎn)已經(jīng)互相關(guān)聯(lián)起來了!
如果你想將某個(gè)節(jié)點(diǎn)移除集群,以移除節(jié)點(diǎn)3為例,可以按照如下方式進(jìn)行操作!
# 首先停止要移除的節(jié)點(diǎn)服務(wù)
rabbitmqctl stop
# 移除節(jié)點(diǎn)3
rabbitmqctl -n rabbit@node1 forget_cluster_node rabbit@node3
如果移除之后,無法啟動(dòng) rabbitMQ,刪除已有 mnesia 信息!
rm -rf /var/lib/rabbitmq/mnesia
然后再次重啟服務(wù)即可!
#加入時(shí)候設(shè)置節(jié)點(diǎn)為內(nèi)存節(jié)點(diǎn)(默認(rèn)加入的為磁盤節(jié)點(diǎn))
rabbitmqctl join_cluster rabbit@node1 --ram
其中--ram
指的是作為內(nèi)存節(jié)點(diǎn),如果不加,那就默認(rèn)為磁盤節(jié)點(diǎn)。
如果節(jié)點(diǎn)在集群中已經(jīng)是磁盤節(jié)點(diǎn)了,通過以下命令可以將節(jié)點(diǎn)改成內(nèi)存節(jié)點(diǎn):
# 停止rabbitmq服務(wù)
rabbitmqctl stop_app
# 更改節(jié)點(diǎn)為內(nèi)存節(jié)點(diǎn)
rabbitmqctl change_cluster_node_type ram
# 開啟rabbitmq服務(wù)
rabbitmqctl start_app
上面我們提到,在默認(rèn)情況下,隊(duì)列只會(huì)保存在其中一個(gè)節(jié)點(diǎn)上,當(dāng)節(jié)點(diǎn)發(fā)生故障時(shí),盡管所有元數(shù)據(jù)信息都可以從磁盤節(jié)點(diǎn)上將元數(shù)據(jù)恢復(fù)到本節(jié)點(diǎn)上,但是內(nèi)存節(jié)點(diǎn)的隊(duì)列消息內(nèi)容就不行了,這樣就會(huì)導(dǎo)致消息的丟失。
RabbitMQ 很早就意識(shí)到這個(gè)問題,在 2.6 以后的版本中增加了隊(duì)列冗余選項(xiàng):鏡像隊(duì)列。
所謂鏡像隊(duì)列,其實(shí)就是主隊(duì)列(master)依然是僅存在于一個(gè)節(jié)點(diǎn)上,通過關(guān)聯(lián)的 rabbitMQ 服務(wù)器,從主隊(duì)列同步消息到各個(gè)節(jié)點(diǎn),也就是所謂的主從模式,將主隊(duì)列的消息進(jìn)行備份處理。
如果主隊(duì)列沒有發(fā)生故障,那么其工作流程跟普通隊(duì)列一樣,生產(chǎn)者和消費(fèi)者不會(huì)感知其變化,當(dāng)發(fā)布消息時(shí),依然是路由到主隊(duì)列中,而主隊(duì)列通過類似廣播的機(jī)制,將消息擴(kuò)散同步至其余從隊(duì)列中,這就有點(diǎn)像 fanout 交換器一樣。而消費(fèi)者依然是從主隊(duì)列中讀取消息。
一旦主隊(duì)列發(fā)生故障,集群就會(huì)從最老的一個(gè)從隊(duì)列選舉為新的主隊(duì)列,這也就實(shí)現(xiàn)了隊(duì)列的高可用了,但我們切記不要濫用這個(gè)機(jī)制,在上面也說了,隊(duì)列的冗余操作會(huì)導(dǎo)致不能通過擴(kuò)展節(jié)點(diǎn)增加存儲(chǔ)空間,而且會(huì)造成性能瓶頸。
命令格式如下:
rabbitmqctl set_policy [-p Vhost] Name Pattern Definition [Priority]
參數(shù)介紹:
-p Vhost: 可選參數(shù),針對(duì)指定vhost下的queue進(jìn)行設(shè)置
Name: policy的名稱
Pattern: queue的匹配模式(正則表達(dá)式)
Definition: 鏡像定義,包括三個(gè)部分ha-mode, ha-params, ha-sync-mode
ha-mode: 指明鏡像隊(duì)列的模式,有效值為 all/exactly/nodes
all: 表示在集群中所有的節(jié)點(diǎn)上進(jìn)行鏡像
exactly: 表示在指定個(gè)數(shù)的節(jié)點(diǎn)上進(jìn)行鏡像,節(jié)點(diǎn)的個(gè)數(shù)由ha-params指定
nodes: 表示在指定的節(jié)點(diǎn)上進(jìn)行鏡像,節(jié)點(diǎn)名稱通過ha-params指定
ha-params: ha-mode模式需要用到的參數(shù)
ha-sync-mode: 進(jìn)行隊(duì)列中消息的同步方式,有效值為automatic和manual
priority: 可選參數(shù),policy的優(yōu)先級(jí)
舉個(gè)例子,聲明名為ha-all
的策略,它與名稱以ha
開頭的隊(duì)列相匹配,并將鏡像配置到集群中的所有節(jié)點(diǎn):
rabbitmqctl set_policy ha-all "^" '{"ha-mode":"all"}'
類似操作很多,具體使用可以參考官方 api。
HAProxy 提供高可用性、負(fù)載均衡以及基于TCP和HTTP應(yīng)用的代理,支持虛擬主機(jī),它是免費(fèi)、快速并且可靠的一種解決方案。根據(jù)官方數(shù)據(jù),其最高極限支持10G的并發(fā)。HAProxy支持從4層至7層的網(wǎng)絡(luò)交換,即覆蓋所有的 TCP 協(xié)議。就是說,Haproxy 甚至還支持 MySQL 的均衡負(fù)載。為了實(shí)現(xiàn) RabbitMQ 集群的軟負(fù)載均衡,這里可以選擇HAProxy。
HAProxy 的安裝也很簡單,單獨(dú)部署在一臺(tái)服務(wù)器上,通過如下命令即可安裝完成!
yum install haproxy
編輯 HAProxy 配置文件:
vim /etc/haproxy/haproxy.cfg
我們只需要在文件末尾加上如下配置即可!
#綁定配置
listen rabbitmq_cluster
bind 0.0.0.0:5672
#配置TCP模式
mode tcp
#加權(quán)輪詢
balance roundrobin
#RabbitMQ集群節(jié)點(diǎn)配置
server rmq_node1 197.168.24.206:5672 check inter 5000 rise 2 fall 3 weight 1
server rmq_node2 197.168.24.233:5672 check inter 5000 rise 2 fall 3 weight 1
server rmq_node3 197.168.24.234:5672 check inter 5000 rise 2 fall 3 weight 1
#haproxy監(jiān)控頁面地址
listen monitor
bind 0.0.0.0:8100
mode http
option httplog
stats enable
stats uri /stats
stats refresh 5s
綁定配置參數(shù)說明:
bind
:這里定義了客戶端連接連接 IP 地址和端口號(hào),用于客戶端連接balance roundrobin
:表示加權(quán)輪詢負(fù)載均衡算法RabbitMQ 集群節(jié)點(diǎn)配置說明:
server rmq_node1
:定義HAProxy內(nèi)RabbitMQ服務(wù)的標(biāo)識(shí)197.168.24.206:5672
:標(biāo)識(shí)了后端RabbitMQ的服務(wù)地址check inter 5000
:表示每隔多少毫秒檢查RabbitMQ服務(wù)是否可用,示例參數(shù)值為 5000rise 2
:表示 RabbitMQ 服務(wù)在發(fā)生故障之后,需要多少次健康檢查才能被再次確認(rèn)可用,示例參數(shù)值為 2fall 2
:表示需要經(jīng)歷多少次失敗的健康檢查之后,HAProxy 才會(huì)停止使用此RabbitMQ服務(wù),示例參數(shù)值為 2weight 1
:表示權(quán)重比例,值越低,會(huì)優(yōu)先進(jìn)行數(shù)據(jù)分配,示例參數(shù)值為 1啟動(dòng) HAProxy:
/usr/sbin/haproxy -f /etc/haproxy/haproxy.cfg
登錄http://ip:8100/stats
web 管理界面,即可進(jìn)行監(jiān)控查看!
如果是配置了 HAProxy 代理服務(wù)器,可以直接使用 HAProxy 代理服務(wù)器地址即可!
//ConnectionFactory創(chuàng)建MQ的物理連接
connectionFactory = new ConnectionFactory();
connectionFactory.setHost("197.168.24.207"); //代理服務(wù)器地址
connectionFactory.setPort(5672); //代理服務(wù)器端口
connectionFactory.setUsername("admin"); //guest只能在本機(jī)進(jìn)行訪問,通過代理服務(wù)器發(fā)送消息時(shí)需要重新建立用戶
connectionFactory.setPassword("admin"); //guest
connectionFactory.setVirtualHost("/"); //虛擬主機(jī)
如果沒有代理服務(wù)器,使用Spring
的CachingConnectionFactory
類進(jìn)行配置。
以SpringBoot
項(xiàng)目為例,配置文件如下:
spring.rabbitmq.addresses=197.168.24.206:5672,197.168.24.233:5672,197.168.24.234:5672
spring.rabbitmq.username=guest
spring.rabbitmq.password=guest
spring.rabbitmq.virtual-host=/
RabbitConfig
配置類如下:
@Configuration
public class RabbitConfig {
/**
* 初始化連接工廠
* @param addresses
* @param userName
* @param password
* @param vhost
* @return
*/
@Bean
ConnectionFactory connectionFactory(@Value("${spring.rabbitmq.addresses}") String addresses,
@Value("${spring.rabbitmq.username}") String userName,
@Value("${spring.rabbitmq.password}") String password,
@Value("${spring.rabbitmq.virtual-host}") String vhost) {
CachingConnectionFactory connectionFactory = new CachingConnectionFactory();
connectionFactory.setAddresses(addresses);
connectionFactory.setUsername(userName);
connectionFactory.setPassword(password);
connectionFactory.setVirtualHost(vhost);
return connectionFactory;
}
/**
* 重新實(shí)例化 RabbitAdmin 操作類
* @param connectionFactory
* @return
*/
@Bean
public RabbitAdmin rabbitAdmin(ConnectionFactory connectionFactory){
return new RabbitAdmin(connectionFactory);
}
/**
* 重新實(shí)例化 RabbitTemplate 操作類
* @param connectionFactory
* @return
*/
@Bean
public RabbitTemplate rabbitTemplate(ConnectionFactory connectionFactory){
RabbitTemplate rabbitTemplate=new RabbitTemplate(connectionFactory);
//數(shù)據(jù)轉(zhuǎn)換為json存入消息隊(duì)列
rabbitTemplate.setMessageConverter(new Jackson2JsonMessageConverter());
return rabbitTemplate;
}
}
關(guān)于RabbitMQ集群高可用原理及實(shí)戰(zhàn)部署是怎樣的就分享到這里了,希望以上內(nèi)容可以對(duì)大家有一定的幫助,可以學(xué)到更多知識(shí)。如果覺得文章不錯(cuò),可以把它分享出去讓更多的人看到。