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