當(dāng)前位置:首頁(yè) > IT技術(shù) > Web編程 > 正文

RabbitMQ從零到集群高可用.NetCore(.NET5)-高可用集群構(gòu)建落地
2021-10-22 09:52:26

?

?

一、集群架構(gòu)簡(jiǎn)介

當(dāng)單臺(tái) RabbitMQ 服務(wù)器的處理消息的能力達(dá)到瓶頸時(shí),此時(shí)可以通過(guò) RabbitMQ 集群來(lái)進(jìn)行擴(kuò)展,從而達(dá)到提升吞吐量的目的。RabbitMQ 集群是一個(gè)或多個(gè)節(jié)點(diǎn)的邏輯分組,集群中的每個(gè)節(jié)點(diǎn)都是對(duì)等的,每個(gè)節(jié)點(diǎn)共享所有的用戶,虛擬主機(jī),隊(duì)列,交換器,綁定關(guān)系,運(yùn)行時(shí)參數(shù)和其他分布式狀態(tài)等信息。一個(gè)高可用,負(fù)載均衡的 RabbitMQ 集群架構(gòu)應(yīng)類(lèi)似下圖:

RabbitMQ從零到集群高可用.NetCore(.NET5)-高可用集群構(gòu)建落地_erlang

?

?

?解析說(shuō)明:

最下面層是RabbitMQ的集群,沒(méi)有ha鏡像時(shí)是普通集群,普通集群的缺點(diǎn)是掛了一個(gè)機(jī)器,以這個(gè)機(jī)器為根的隊(duì)列就無(wú)法使用了(一個(gè)隊(duì)列的數(shù)據(jù)只會(huì)存在一個(gè)節(jié)點(diǎn)),無(wú)法實(shí)現(xiàn)高可用。

所以把隊(duì)列變成鏡像隊(duì)列,這樣每個(gè)節(jié)點(diǎn)都會(huì)有一份完整的數(shù)據(jù),有節(jié)點(diǎn)掛了也不影響使用,實(shí)現(xiàn)了高可用,但RabbitMQ集群本身沒(méi)有實(shí)現(xiàn)負(fù)載均衡,也就是說(shuō)對(duì)于一個(gè)三節(jié)點(diǎn)的集群,

每個(gè)節(jié)點(diǎn)的負(fù)載可能都是不相同的。

HAProxy層的作用就是為了實(shí)現(xiàn)RabbitMQ集群的負(fù)載均衡,但一個(gè)節(jié)點(diǎn)的話顯然也不能高可用,所以需要兩個(gè)HAProxy實(shí)現(xiàn)HaProxy的高可用,但沒(méi)法實(shí)現(xiàn)自動(dòng)的故障轉(zhuǎn)移,就是HAProxy1掛了,

需要手動(dòng)把ip地址改成HAProxy2的。

所以需要用到KeepAlived,它通常由一主一備兩個(gè)節(jié)點(diǎn)組成,同一時(shí)間內(nèi)只有主節(jié)點(diǎn)會(huì)提供對(duì)外服務(wù),并同時(shí)提供一個(gè)虛擬的 IP 地址 (Virtual Internet Protocol Address ,簡(jiǎn)稱(chēng) VIP),可以避免暴露真實(shí)ip 。 如果主節(jié)點(diǎn)故障,那么備份節(jié)點(diǎn)會(huì)自動(dòng)接管 VIP 并成為新的主節(jié)點(diǎn) ,直到原有的主節(jié)點(diǎn)恢復(fù)。

生產(chǎn)環(huán)境架構(gòu)應(yīng)該為:

機(jī)器1:RabbitMQ1,機(jī)器2:RabbitMQ2,機(jī)器3:RabbitMQ3,機(jī)器4:HAProxy+keeplived(主),機(jī)器5(HAProxy+keeplived(備)。

這里資源原因只有3臺(tái)機(jī)器,所以搭建架構(gòu)為:

172.16.2.84(rabbit1)

RabbitMQ1,HAProxy+keeplived(主)

172.16.2.85(rabbit2)

RabbitMQ2,HAProxy+keeplived(備)

172.16.2.86(rabbit3)

RabbitMQ3

?

二、普通集群搭建

1)各個(gè)節(jié)點(diǎn)分別安裝RabbitMQ

這里前面的章節(jié)功能使用是用的docker安裝,這里集群不用docker,因?yàn)閐ocker安裝會(huì)有很多的映射,很容易擾亂,裝有docker的rabbitmq的,需要先把docker停掉。

這里準(zhǔn)備了3臺(tái)機(jī)器,172.16.2.84(rabbit1),172.16.2.85(rabbit2),172.16.2.86(rabbit3),為了避免混淆,下面都會(huì)用rabbit1,rabbit2,rabbit3稱(chēng)呼。

rabbit1,rabbit2,rabbit3都執(zhí)行一遍下面的安裝。

1.1安裝前,先修改hostname,因?yàn)閞abbitmq集群通訊需要用hostname

rabbit1機(jī)器執(zhí)行



hostnamectl set-hostname rabbit1 --static


rabbit2機(jī)器執(zhí)行



hostnamectl set-hostname rabbit2 --static


rabbit3機(jī)器執(zhí)行



hostnamectl set-hostname rabbit3 --static


rabbit1,rabbit2,rabbit3執(zhí)行同樣操作



#修改hosts,因?yàn)閞abbitmq通訊要通過(guò)hostname
vi /etc/hosts


1.2把ip對(duì)應(yīng)hostname添加到后面

172.16.2.84 rabbit1

172.16.2.85 rabbit2

172.16.2.86 rabbit3

RabbitMQ從零到集群高可用.NetCore(.NET5)-高可用集群構(gòu)建落地_erlang_02



# 重啟網(wǎng)絡(luò)
systemctl restart network
# 重啟機(jī)器
init 6


?

1.3安裝Erlang



#第一步 運(yùn)行package cloud提供的erlang安裝腳本
curl -s https://packagecloud.io/install/repositories/rabbitmq/erlang/script.rpm.sh | sudo bash

#第二步 安裝erlang
yum install erlang

#第三步 查看erlang版本號(hào),在命令行直接輸入erl
erl


1.4安裝RabbitMQ



#第一步 先導(dǎo)入兩個(gè)key
rpm --import https://packagecloud.io/rabbitmq/rabbitmq-server/gpgkey
rpm --import https://packagecloud.io/gpg.key

#第二步 運(yùn)行package cloud提供的rabbitmq安裝腳本
curl -s https://packagecloud.io/install/repositories/rabbitmq/rabbitmq-server/script.rpm.sh | sudo bash

#第三步 下載rabbit安裝文件
wget https://github.com/rabbitmq/rabbitmq-server/releases/download/v3.9.5/rabbitmq-server-3.9.5-1.el8.noarch.rpm

#第四步
rpm --import https://www.rabbitmq.com/rabbitmq-release-signing-key.asc

#第五步 rabbitMQ依賴(lài)
yum -y install epel-release
yum -y install socat

#第六步 安裝
rpm -ivh rabbitmq-server-3.9.5-1.el8.noarch.rpm

#第七步 啟用管理平臺(tái)插件,啟用插件后,可以可視化管理RabbitMQ
rabbitmq-plugins enable rabbitmq_management

#第八步 啟動(dòng)應(yīng)用
systemctl start rabbitmq-server


上面rabbitmq的版本號(hào)安裝文件地址:??https://github.com/rabbitmq/rabbitmq-server/releases/??

1.5設(shè)置訪問(wèn)權(quán)限



#創(chuàng)建管理員賬戶
rabbitmqctl add_user admin 123456
#設(shè)置注冊(cè)的賬戶為管理員
rabbitmqctl set_user_tags admin administrator
#授權(quán)遠(yuǎn)程訪問(wèn)
rabbitmqctl set_permissions -p / admin "." "." ".*"
#重啟服務(wù)
systemctl restart rabbitmq-server


注意這里關(guān)了防火墻,如果開(kāi)啟防火墻的話,需要把15672端口開(kāi)放出來(lái)



#查看防火墻狀態(tài)
systemctl status firewalld
#關(guān)閉防火墻
systemctl stop firewalld


到這里,3臺(tái)機(jī)器都安裝好了RabbitMQ。

RabbitMQ從零到集群高可用.NetCore(.NET5)-高可用集群構(gòu)建落地_.NET5_03

?

?

?

?

?

?

?

?

2)把節(jié)點(diǎn)加入集群

2.1停止服務(wù)rabbit2,rabbit3



#停止全部服務(wù)
systemctl stop rabbitmq-server


?

2.2拷貝cookie

將rabbit1上的.erlang.cookie文件拷貝到其他兩臺(tái)主機(jī)上。該 cookie 文件相當(dāng)于密鑰令牌,集群中的 RabbitMQ 節(jié)點(diǎn)需要通過(guò)交換密鑰令牌以獲得相互認(rèn)證,因此處于同一集群的所有節(jié)點(diǎn)需要具有相同的密鑰令牌,否則在搭建過(guò)程中會(huì)出現(xiàn) Authentication Fail 錯(cuò)誤,只要保證這3臺(tái)機(jī)器中的.erlang.cookie內(nèi)的密鑰字符串一致即可,這里把rabbit1的密鑰復(fù)制到rabbit2,rabbit3。

3個(gè)機(jī)器都給.erlang.cookie 400權(quán)限。



#給600權(quán)限
chmod 600 /var/lib/rabbitmq/.erlang.cookie


rabbit1機(jī)器



#編輯文件
vi /var/lib/rabbitmq/.erlang.cookie


RabbitMQ從零到集群高可用.NetCore(.NET5)-高可用集群構(gòu)建落地_.NET5_04

?

?

?把內(nèi)容復(fù)制出來(lái),修改rabbit2,rabbit3這個(gè)文件的值為這個(gè)。

啟動(dòng)服務(wù)



#開(kāi)啟全部服務(wù)
systemctl start rabbitmq-server


?

2.3集群搭建

RabbitMQ 集群的搭建需要選擇其中任意一個(gè)節(jié)點(diǎn)為基準(zhǔn),將其它節(jié)點(diǎn)逐步加入。這里我們以 rabbit1為基準(zhǔn)節(jié)點(diǎn),將 rabbit2 和 rabbit3 加入集群。在 rabbit2和 rabbit3上執(zhí)行以下命令:



# 1.停止服務(wù)
rabbitmqctl stop_app
# 2.重置狀態(tài)(需要更改節(jié)點(diǎn)類(lèi)型的時(shí)候執(zhí)行,首次不需要執(zhí)行,除非你節(jié)點(diǎn)是以disk加入集群的)
rabbitmqctl reset
# 3.節(jié)點(diǎn)加入
#rabbitmqctl join_cluster --ram rabbit@rabbit1
rabbitmqctl join_cluster rabbit@rabbit1
# 4.啟動(dòng)服務(wù)
rabbitmqctl start_app


?

??join_cluster????命令有一個(gè)可選的參數(shù) --??ram???,該參數(shù)代表新加入的節(jié)點(diǎn)是內(nèi)存節(jié)點(diǎn),默認(rèn)是磁盤(pán)節(jié)點(diǎn)。如果是內(nèi)存節(jié)點(diǎn),則所有的隊(duì)列、交換器、綁定關(guān)系、用戶、訪問(wèn)權(quán)限和 vhost 的元數(shù)據(jù)都將存儲(chǔ)在內(nèi)存中,如果是磁盤(pán)節(jié)點(diǎn),則存儲(chǔ)在磁盤(pán)中。內(nèi)存節(jié)點(diǎn)可以有更高的性能,但其重啟后所有配置信息都會(huì)丟失,因此RabbitMQ 要求在集群中至少有一個(gè)磁盤(pán)節(jié)點(diǎn),其他節(jié)點(diǎn)可以是內(nèi)存節(jié)點(diǎn),大多數(shù)情況下RabbitMQ 的性能都是夠用的,可以采用默認(rèn)的磁盤(pán)節(jié)點(diǎn)的形式。

另外,如果節(jié)點(diǎn)以磁盤(pán)節(jié)點(diǎn)的形式加入,則需要先使用???reset????命令進(jìn)行重置,然后才能加入現(xiàn)有群集,重置節(jié)點(diǎn)會(huì)刪除該節(jié)點(diǎn)上存在的所有的歷史資源和數(shù)據(jù)。采用內(nèi)存節(jié)點(diǎn)的形式加入時(shí)可以略過(guò)???reset???這一步,因?yàn)閮?nèi)存上的數(shù)據(jù)本身就不是持久化的。

操作上面的,一個(gè)普通集群就搭建成功了,打開(kāi)rabbit管理界面(隨便打開(kāi)一個(gè)都是一樣的)。

RabbitMQ從零到集群高可用.NetCore(.NET5)-高可用集群構(gòu)建落地_高可用_05

?

?

?

3)代碼演示普通集群的問(wèn)題

普通集群中, 第一次創(chuàng)建隊(duì)列時(shí),會(huì)隨機(jī)選一個(gè)節(jié)點(diǎn)作為根節(jié)點(diǎn),這個(gè)節(jié)點(diǎn)會(huì)存儲(chǔ)隊(duì)列的信息(交互機(jī),路由,隊(duì)列名等)和隊(duì)列的數(shù)據(jù),其它兩個(gè)節(jié)點(diǎn)只會(huì)同步根節(jié)點(diǎn)的元信息(交換機(jī),路由,隊(duì)列名)等,

但不會(huì)存儲(chǔ)隊(duì)列的數(shù)據(jù),他們是通過(guò)元信息找到根節(jié)點(diǎn)讀寫(xiě)消息。

RabbitMQ從零到集群高可用.NetCore(.NET5)-高可用集群構(gòu)建落地_RabbitMQ_06

?

?

?例如集群選擇了rabbit2作為根節(jié)點(diǎn),那么數(shù)據(jù)存儲(chǔ)在rabbit2,rabbit1和rabbit3是沒(méi)有數(shù)據(jù)的,那么如果rabbit2宕機(jī)了,隊(duì)列里面的數(shù)據(jù)就取不到了。

代碼演示,.NetCore5.0讀取集群連接代碼。



/// <summary>
/// 獲取集群連接對(duì)象
/// </summary>
/// <returns></returns>
public static IConnection GetClusterConnection()
{
var factory = new ConnectionFactory
{
UserName = "admin",//賬戶
Password = "123456",//密碼
VirtualHost = "/" //虛擬機(jī)
};
List<AmqpTcpEndpoint> list = new List<AmqpTcpEndpoint>()
{
new AmqpTcpEndpoint(){HostName="172.16.2.84",Port=5672},
new AmqpTcpEndpoint(){HostName="172.16.2.85",Port=5672},
new AmqpTcpEndpoint(){HostName="172.16.2.86",Port=5672}
};
return factory.CreateConnection(list);
}


生產(chǎn)者代碼:



/// <summary>
/// 工作隊(duì)列模式
/// </summary>
public static void WorkerSendMsg()
{
string queueName = "worker_order";//隊(duì)列名
//創(chuàng)建連接
using (var connection = RabbitMQHelper.GetClusterConnection())
{
//創(chuàng)建信道
using (var channel = connection.CreateModel())
{
//創(chuàng)建隊(duì)列
channel.QueueDeclare(queueName, durable: true, exclusive: false, autoDelete: false, arguments: null);
IBasicProperties properties = channel.CreateBasicProperties();
properties.Persistent = true; //消息持久化
for ( var i=0;i<10;i++)
{
string message = $"Hello RabbitMQ MessageHello,{i+1}";
var body = Encoding.UTF8.GetBytes(message);
//發(fā)送消息到rabbitmq
channel.BasicPublish(exchange: "", routingKey: queueName, mandatory: false, basicProperties: properties, body);
Console.WriteLine($"發(fā)送消息到隊(duì)列:{queueName},內(nèi)容:{message}");
}
}
}
}


執(zhí)行:

RabbitMQ從零到集群高可用.NetCore(.NET5)-高可用集群構(gòu)建落地_數(shù)據(jù)_07

?

?

?查看RabbitMQ管理界面

RabbitMQ從零到集群高可用.NetCore(.NET5)-高可用集群構(gòu)建落地_高可用_08

?

?

?RabbitMQ選擇了rabbit3作為根節(jié)點(diǎn),現(xiàn)在試一下停止rabbit3節(jié)點(diǎn)



#停止服務(wù)
rabbitmqctl stop_app


RabbitMQ從零到集群高可用.NetCore(.NET5)-高可用集群構(gòu)建落地_高可用_09

?

?

?發(fā)現(xiàn)隊(duì)列不可用了,啟動(dòng)rabbit3,再試一下停止rabbit2

RabbitMQ從零到集群高可用.NetCore(.NET5)-高可用集群構(gòu)建落地_.NET5_10

?

?

?

發(fā)現(xiàn)隊(duì)列正常,試下消費(fèi)數(shù)據(jù):



public static void WorkerConsumer()
{
string queueName = "worker_order";
var connection = RabbitMQHelper.GetClusterConnection();
{
//創(chuàng)建信道
var channel = connection.CreateModel();
{
//創(chuàng)建隊(duì)列
channel.QueueDeclare(queueName, durable: true, exclusive: false, autoDelete: false, arguments: null);
var consumer = new EventingBasicConsumer(channel);
///prefetchCount:1來(lái)告知RabbitMQ,不要同時(shí)給一個(gè)消費(fèi)者推送多于 N 個(gè)消息,也確保了消費(fèi)速度和性能
///global:是否設(shè)為全局的
///prefetchSize:單條消息大小,通常設(shè)0,表示不做限制
//是autoAck=false才會(huì)有效
channel.BasicQos(prefetchSize: 0, prefetchCount: 1, global: true);
int i = 1;
int index = new Random().Next(10);
consumer.Received += (model, ea) =>
{
//處理業(yè)務(wù)
var message = Encoding.UTF8.GetString(ea.Body.ToArray());
Console.WriteLine($"{i},消費(fèi)者:{index},隊(duì)列{queueName}消費(fèi)消息長(zhǎng)度:{message.Length}");
Thread.Sleep(1000);
channel.BasicAck(ea.DeliveryTag, false); //消息ack確認(rèn),告訴mq這條隊(duì)列處理完,可以從mq刪除了
i++;
};
channel.BasicConsume(queueName, autoAck: false, consumer);
}
}
}


?RabbitMQ從零到集群高可用.NetCore(.NET5)-高可用集群構(gòu)建落地_數(shù)據(jù)_11

?

如果根節(jié)點(diǎn)掛了,再往集群發(fā)送數(shù)據(jù),RabbitMQ又會(huì)從其余的選一個(gè)作為根節(jié)點(diǎn),就可能的情況是,多個(gè)節(jié)點(diǎn)都存有不同隊(duì)列的數(shù)據(jù)。

RabbitMQ從零到集群高可用.NetCore(.NET5)-高可用集群構(gòu)建落地_.NET5_12

?

?

到這里,可以看到普通集群并不可以高可用, 根節(jié)點(diǎn)掛了,在這個(gè)節(jié)點(diǎn)上的隊(duì)列也不可用了。但三個(gè)節(jié)點(diǎn)都沒(méi)問(wèn)題的時(shí)候,可以提高并發(fā)和隊(duì)列的負(fù)載。

要實(shí)現(xiàn)高可用,就要用到了鏡像集群。

三、鏡像集群

鏡像集群,在每個(gè)節(jié)點(diǎn)上都同步一份鏡像數(shù)據(jù),相當(dāng)于每個(gè)節(jié)點(diǎn)都有一份完整的數(shù)據(jù),這樣有節(jié)點(diǎn)宕機(jī)了,還能正常提供RabbitMQ服務(wù)。

變鏡像集群很簡(jiǎn)單,在上面普通集群的基礎(chǔ)上,在任意一個(gè)節(jié)點(diǎn)下執(zhí)行



#把集群變成鏡像集群
rabbitmqctl set_policy ha-all "^" '{"ha-mode":"all"}'


執(zhí)行完,看回隊(duì)列,多了ha標(biāo)識(shí)。

RabbitMQ從零到集群高可用.NetCore(.NET5)-高可用集群構(gòu)建落地_erlang_13

?

?

?

?RabbitMQ從零到集群高可用.NetCore(.NET5)-高可用集群構(gòu)建落地_RabbitMQ_14

?

?

四、HAProxy環(huán)境搭建。

4.1下載

HAProxy 官方下載地址為:??https://www.haproxy.org/#down????,如果這個(gè)網(wǎng)站無(wú)法訪問(wèn),也可以從???https://src.fedoraproject.org/repo/pkgs/haproxy/???上進(jìn)行下載。



#下載haproxy
wget https://www.haproxy.org/download/2.4/src/haproxy-2.4.3.tar.gz


解壓



#解壓
tar xf haproxy-2.4.3.tar.gz


4.2編譯

安裝gcc



yum install gcc


進(jìn)入解壓后根目錄,執(zhí)行下面的編譯命令:



#進(jìn)入haproxy-2.4.3目錄后執(zhí)行
make TARGET=linux-glibc PREFIX=/usr/app/haproxy-2.4.3
make install PREFIX=/usr/app/haproxy-2.4.3


4.3配置環(huán)境變量



#編輯文件
vi /etc/profile



#把這兩項(xiàng)加上
export HAPROXY_HOME=/usr/app/haproxy-2.4.3
export PATH=$PATH:$HAPROXY_HOME/sbin


RabbitMQ從零到集群高可用.NetCore(.NET5)-高可用集群構(gòu)建落地_數(shù)據(jù)_15

?

?

?使得配置的環(huán)境變量立即生效:



source /etc/profile


4.4負(fù)載均衡配置

?新建配置文件haproxy.cfg,這里我新建的位置為:/etc/haproxy/haproxy.cfg,文件內(nèi)容如下:



global
# 日志輸出配置、所有日志都記錄在本機(jī),通過(guò) local0 進(jìn)行輸出
log 127.0.0.1 local0 info
# 最大連接數(shù)
maxconn 4096
# 改變當(dāng)前的工作目錄
chroot /usr/app/haproxy-2.4.3
# 以指定的 UID 運(yùn)行 haproxy 進(jìn)程
uid 99
# 以指定的 GID 運(yùn)行 haproxy 進(jìn)程
gid 99
# 以守護(hù)進(jìn)行的方式運(yùn)行
daemon
# 當(dāng)前進(jìn)程的 pid 文件存放位置
pidfile /usr/app/haproxy-2.4.3/haproxy.pid

# 默認(rèn)配置
defaults
# 應(yīng)用全局的日志配置
log global
# 使用4層代理模式,7層代理模式則為"http"
mode tcp
# 日志類(lèi)別
option tcplog
# 不記錄健康檢查的日志信息
option dontlognull
# 3次失敗則認(rèn)為服務(wù)不可用
retries 3
# 每個(gè)進(jìn)程可用的最大連接數(shù)
maxconn 2000
# 連接超時(shí)
timeout connect 5s
# 客戶端超時(shí)
timeout client 120s
# 服務(wù)端超時(shí)
timeout server 120s

# 綁定配置
listen rabbitmq_cluster
bind :5671
# 配置TCP模式
mode tcp
# 采用加權(quán)輪詢的機(jī)制進(jìn)行負(fù)載均衡
balance roundrobin
# RabbitMQ 集群節(jié)點(diǎn)配置
server rabbit1 rabbit1:5672 check inter 5000 rise 2 fall 3 weight 1
server rabbit2 rabbit2:5672 check inter 5000 rise 2 fall 3 weight 1
server rabbit3 rabbit3:5672 check inter 5000 rise 2 fall 3 weight 1

# 配置監(jiān)控頁(yè)面
listen monitor
bind :8100
mode http
option httplog
stats enable
stats uri /stats
stats refresh 5s


上傳上去的,記得打開(kāi)看一下?lián)Q行符有沒(méi)有出行這些符號(hào),如果有要?jiǎng)h掉,不然會(huì)報(bào)錯(cuò)。

RabbitMQ從零到集群高可用.NetCore(.NET5)-高可用集群構(gòu)建落地_高可用_16

?

?

?

負(fù)載均衡的主要配置在???listen rabbitmq_cluster???下,這里指定負(fù)載均衡的方式為加權(quán)輪詢,同時(shí)定義好健康檢查機(jī)制:



?









server rabbit1 rabbit1:5672 check inter 5000 rise 2 fall 3 weight 1


以上配置代表對(duì)地址為 rabbit1:5672 的 rabbit1 節(jié)點(diǎn)每隔 5 秒進(jìn)行一次健康檢查,如果連續(xù)兩次的檢查結(jié)果都是正常,則認(rèn)為該節(jié)點(diǎn)可用,此時(shí)可以將客戶端的請(qǐng)求輪詢到該節(jié)點(diǎn)上;如果連續(xù) 3 次的檢查結(jié)果都不正常,則認(rèn)為該節(jié)點(diǎn)不可用。weight 用于指定節(jié)點(diǎn)在輪詢過(guò)程中的權(quán)重。

?

4.5啟動(dòng)服務(wù)



haproxy -f /etc/haproxy/haproxy.cfg


啟動(dòng)后可以在監(jiān)控頁(yè)面進(jìn)行查看,端口為設(shè)置的 8100,完整地址為:??http://172.16.2.84:8100/stats???,頁(yè)面情況如下:

RabbitMQ從零到集群高可用.NetCore(.NET5)-高可用集群構(gòu)建落地_高可用_17

?

?

?所有節(jié)點(diǎn)都為綠色,代表節(jié)點(diǎn)健康。此時(shí)證明 HAProxy 搭建成功,并已經(jīng)對(duì) RabbitMQ 集群進(jìn)行監(jiān)控。

?

這里已經(jīng)實(shí)現(xiàn)了RabbitMQ的負(fù)載均衡了,代碼怎么通過(guò)Haproxy連接Rabbit集群呢,因?yàn)樯厦媾渲肏aproxy暴露的端口是5671,所以Ip是Haproxy的ip:5671。

連接代碼,可以通過(guò)haproxy1 或haproxy2都可以:



public static IConnection GetConnection()
{

ConnectionFactory factory = new ConnectionFactory
{
HostName = "172.16.2.84",//haproxy ip
Port = 5671,//haproxy 端口
UserName = "admin",//賬號(hào)
Password = "123456",//密碼
VirtualHost = "/" //虛擬主機(jī)
};

return factory.CreateConnection();
}


?

五、KeepAlived 環(huán)境搭建

接著就可以搭建 Keepalived 來(lái)解決 HAProxy 故障轉(zhuǎn)移的問(wèn)題。這里我在 rabbit1 和 rabbit2 上安裝 KeepAlived ,兩臺(tái)主機(jī)上的搭建的步驟完全相同,只是部分配置略有不同,具體如下:

官網(wǎng):??https://www.keepalived.org??

?

5.1安裝



yum install -y keepalived


5.2修改配置文件

安裝了keepalived后,配置文件生成在/etc/keepalived/keepalived.conf

這里先對(duì)keepalived1上keepalived.conf配置文件進(jìn)行修改,完整內(nèi)容如下:



global_defs {
# 路由id,主備節(jié)點(diǎn)不能相同
router_id node1
}

# 自定義監(jiān)控腳本
vrrp_script chk_haproxy {
# 腳本位置
script "/etc/keepalived/haproxy_check.sh"
# 腳本執(zhí)行的時(shí)間間隔
interval 5
weight 10
}

vrrp_instance VI_1 {
# Keepalived的角色,MASTER 表示主節(jié)點(diǎn),BACKUP 表示備份節(jié)點(diǎn)
state MASTER
# 指定監(jiān)測(cè)的網(wǎng)卡,可以使用 ip addr 進(jìn)行查看
interface ens33
# 虛擬路由的id,主備節(jié)點(diǎn)需要設(shè)置為相同
virtual_router_id 1
# 優(yōu)先級(jí),主節(jié)點(diǎn)的優(yōu)先級(jí)需要設(shè)置比備份節(jié)點(diǎn)高
priority 100
# 設(shè)置主備之間的檢查時(shí)間,單位為秒
advert_int 1
# 定義驗(yàn)證類(lèi)型和密碼
authentication {
auth_type PASS
auth_pass 123456
}

# 調(diào)用上面自定義的監(jiān)控腳本
track_script {
chk_haproxy
}

virtual_ipaddress {
# 虛擬IP地址,可以設(shè)置多個(gè)
172.16.2.200
}
}


以上配置定義了 keepalived1上的 Keepalived 節(jié)點(diǎn)為 MASTER 節(jié)點(diǎn),并設(shè)置對(duì)外提供服務(wù)的虛擬 IP 為 172.16.2.200。此外最主要的是定義了通過(guò)???haproxy_check.sh???來(lái)對(duì) HAProxy 進(jìn)行監(jiān)控,這個(gè)腳本需要我們自行創(chuàng)建,內(nèi)容如下:



#!/bin/bash
# 判斷haproxy是否已經(jīng)啟動(dòng)
if [ `ps -C haproxy --no-header | wc -l` -eq 0 ] ; then
#如果沒(méi)有啟動(dòng),則啟動(dòng)
haproxy -f /etc/haproxy/haproxy.cfg
fi

#睡眠3秒以便haproxy完全啟動(dòng)
sleep 3

#如果haproxy還是沒(méi)有啟動(dòng),此時(shí)需要將本機(jī)的keepalived服務(wù)停掉,以便讓VIP自動(dòng)漂移到另外一臺(tái)haproxy
if [ `ps -C haproxy --no-header | wc -l` -eq 0 ]; then
systemctl stop keepalived
fi


創(chuàng)建后為其賦予執(zhí)行權(quán)限:



chmod +x /etc/keepalived/haproxy_check.sh


這個(gè)腳本主要用于判斷 HAProxy 服務(wù)是否正常,如果不正常且無(wú)法啟動(dòng),此時(shí)就需要將本機(jī) Keepalived 關(guān)閉,從而讓虛擬 IP 漂移到備份節(jié)點(diǎn)。

備份節(jié)點(diǎn)(keepalived2)的配置與主節(jié)點(diǎn)基本相同,但是需要修改其 state 為 BACKUP;同時(shí)其優(yōu)先級(jí) priority 需要比主節(jié)點(diǎn)低。完整配置如下:



global_defs {
# 路由id,主備節(jié)點(diǎn)不能相同
router_id node2

}

vrrp_script chk_haproxy {
script "/etc/keepalived/haproxy_check.sh"
interval 5
weight 10
}

vrrp_instance VI_1 {
# BACKUP 表示備份節(jié)點(diǎn)
state BACKUP
interface ens33
virtual_router_id 1
# 優(yōu)先級(jí),備份節(jié)點(diǎn)要比主節(jié)點(diǎn)低
priority 50
advert_int 1
authentication {
auth_type PASS
auth_pass 123456
}

track_script {
chk_haproxy
}

virtual_ipaddress {
172.16.2.200
}
}


haproxy_check.sh文件和keepalived1相同

5.3啟動(dòng)服務(wù)

分別在KeepAlived1和KeepAlived2上啟動(dòng)KeepAlived服務(wù),命令如下:



systemctl start  keepalived


啟動(dòng)后此時(shí) keepAlived1 為主節(jié)點(diǎn),可以在?keepAlived1? 上使用???ip a???命令查看到虛擬 IP 的情況:

RabbitMQ從零到集群高可用.NetCore(.NET5)-高可用集群構(gòu)建落地_RabbitMQ_18

?

?

?此時(shí)只有 keepAlived1?上是存在虛擬 IP 的,而?keepAlived2? 上是沒(méi)有的。

RabbitMQ從零到集群高可用.NetCore(.NET5)-高可用集群構(gòu)建落地_RabbitMQ_19

?

?

?5.4驗(yàn)證故障轉(zhuǎn)移

這里我們驗(yàn)證一下故障轉(zhuǎn)移,因?yàn)榘凑瘴覀兩厦娴臋z測(cè)腳本,如果 HAProxy 已經(jīng)停止且無(wú)法重啟時(shí) KeepAlived 服務(wù)就會(huì)停止,這里我們直接使用以下命令停止 Keepalived1 服務(wù):



systemctl stop keepalived


此時(shí)再次使用???ip a???分別查看,可以發(fā)現(xiàn) keepalived1上的 VIP 已經(jīng)漂移到 keepalived2上,情況如下:

RabbitMQ從零到集群高可用.NetCore(.NET5)-高可用集群構(gòu)建落地_.NET5_20

?

?

此時(shí)對(duì)外服務(wù)的 VIP 依然可用,代表已經(jīng)成功地進(jìn)行了故障轉(zhuǎn)移。至此集群已經(jīng)搭建成功,任何需要發(fā)送或者接受消息的客戶端服務(wù)只需要連接到該 VIP 即可,示例如下:



public static IConnection GetConnection()
{
ConnectionFactory factory = new ConnectionFactory()
{
HostName = "172.16.2.200",//vip
Port = 5671,//haproxy 端口
UserName = "admin",//賬號(hào)
Password = "123456",//密碼
VirtualHost = "/" //虛擬主機(jī)
};

return factory.CreateConnection();
}


?

本文摘自 :https://blog.51cto.com/u

開(kāi)通會(huì)員,享受整站包年服務(wù)立即開(kāi)通 >