RabbitMQ镜像队列模式集群:
- RabbitMQ集群模式非常经典的就是Mirror镜像模式,节点足够的情况下能保证100%数据不丢失,在实际工作中也是用的最多的。并且实现集群非常的简单,一般互联网大厂都会构建这种镜像集群模式
- Mirror镜像队列,目的是为了保证rabbitmq数据的高可靠性解决方案,主要就是实现数据的同步,一般来讲是2-3个节点实现数据同步(对于100%数据可靠性解决方案一般是3节点以上)
在本文中将要搭建的RabbitMQ集群架构如下:
RabbitMQ集群环境节点说明:
机器IP | hostname | 节点说明 | 端口 | 管控台地址 |
---|---|---|---|---|
192.168.243.164 | rabbitmq01 | RabbitMQ Master | 5672 | http://192.168.243.164:15672 |
192.168.243.165 | rabbitmq02 | RabbitMQ Slave | 5672 | http://192.168.243.165:15672 |
192.168.243.166 | rabbitmq03 | RabbitMQ Slave | 5672 | http://192.168.243.166:15672 |
192.168.243.167 | haproxy01 | HAProxy + Keepalived | 8100 | http://192.168.243.167:8100/rabbitmq-stats |
192.168.243.168 | haproxy02 | HAProxy + Keepalived | 8100 | http://192.168.243.168:8100/rabbitmq-stats |
192.168.243.164、192.168.243.165及192.168.243.166这三台机器上需要事先安装好RabbitMQ,具体安装步骤参考如下文章:
这三台机器的hosts
文件均需配置IP与hostname
的映射关系,如下:
$ cat /etc/hosts
192.168.243.164 rabbitmq01
192.168.243.165 rabbitmq02
192.168.243.166 rabbitmq03
接下来我们将这三台RabbitMQ节点组成镜像队列集群。首先,需要将这三个节点的RabbitMQ服务都停止,如果启动了的话:
$ rabbitmqctl stop_app
这里我将192.168.243.164这个节点作为Master角色,把Master节点的.erlang.cookie
文件拷贝到其他两台机器上:
[root@rabbitmq01 ~]# scp /var/lib/rabbitmq/.erlang.cookie 192.168.243.165:/var/lib/rabbitmq/
[root@rabbitmq01 ~]# scp /var/lib/rabbitmq/.erlang.cookie 192.168.243.166:/var/lib/rabbitmq/
然后分别在三个节点上执行如下命令,配置为独立模式:
$ rabbitmq-server -detached
启动master节点:
[root@rabbitmq01 ~]# rabbitmqctl start_app
将salve加入到集群中:
[root@rabbitmq02 ~]# rabbitmqctl join_cluster rabbit@rabbitmq01
Clustering node rabbit@rabbitmq02 with rabbit@rabbitmq01
[root@rabbitmq02 ~]# rabbitmqctl start_app
[root@rabbitmq03 ~]# rabbitmqctl join_cluster rabbit@rabbitmq01
Clustering node rabbit@rabbitmq03 with rabbit@rabbitmq01
[root@rabbitmq03 ~]# rabbitmqctl start_app
-
Tips:如果想让加入集群中的节点是以内存方式存储数据,则需要加上
--ram
参数,否则默认是磁盘存储
完成以上步骤后,此时集群就已经搭建起来了,可以使用如下命令查看集群状态信息:
[root@rabbitmq01 ~]# rabbitmqctl cluster_status
Cluster status of node rabbit@rabbitmq01 ...
Basics
Cluster name: rabbit@rabbitmq01
Disk Nodes
rabbit@rabbitmq01
rabbit@rabbitmq02
rabbit@rabbitmq03
Running Nodes
rabbit@rabbitmq01
rabbit@rabbitmq02
rabbit@rabbitmq03
...
我们可以使用如下命令修改集群名称(默认为第一个节点的名称):
[root@rabbitmq01 ~]# rabbitmqctl set_cluster_name dev_rabbitmq_cluster
Setting cluster name to dev_rabbitmq_cluster ...
[root@rabbitmq01 ~]#
如果以后需要移除集群中的节点可以在master节点上使用如下命令:
$ rabbitmqctl forget_cluster_node rabbit@rabbitmq02
使用浏览器访问集群中任意一个节点的管控台,在“Nodes”一栏中可以看到列出了集群中所有节点的概览信息:
经过以上步骤搭建好普通集群后,此时集群中的节点还是会各自存储各自的数据,数据不会在多个节点中冗余存储,这样其中一个节点挂掉了,那么该节点的数据就无法被消费了。
所以接下来我们还需要配置集群模式为镜像队列模式,将所有队列设置为镜像队列后,队列才会被复制到各个节点上,让各个节点状态保持一致。设置镜像队列策略的命令如下(在任意一个节点上执行):
[root@rabbitmq01 ~]# rabbitmqctl set_policy ha-all "^" '{"ha-mode":"all"}'
Setting policy "ha-all" for pattern "^" to "{"ha-mode":"all"}" with priority "0" for vhost "/" ...
[root@rabbitmq01 ~]#
然后到任意一个节点的管控台上创建一个队列:
此时可以发现其他节点也会复制该队列,代表镜像队列模式配置成功:
RabbitMQ集群整合负载均衡基础组件HAProxy
在上一小节中,我们搭建了RabbitMQ的镜像队列集群,虽然集群节点之间能够同步数据保证高可靠的存储了,但有个问题就是客户端通常只能连接集群中的其中一个节点。这样就会造成集群中只有一个节点在负载所有的读写操作,导致该节点负载过高,而其他节点则无所事事。
因此,我们就需要对RabbitMQ集群做负载均衡,负载均衡的方案通常有服务端负载均衡和客户端负载均衡两种。客户端负载均衡需要我们自己在客户端实现负载均衡算法,或使用第三方的库。服务端负载均衡则是通过一些中间件来实现,例如HAProxy、Nginx、LVS等。
由于本文主要是演示环境的搭建不涉及编码,所以采用的是服务端负载均衡方案,使用HAProxy组件来做RabbitMQ集群的负载均衡组件。
HAProxy是一款提供高可用性、负载均衡以及基于TCP(第四层)和HTTP(第七层)应用的代理软件,支持虚拟主机,它是免费、快速并且可靠的一种解决方案。 HAProxy特别适用于那些负载特大的web站点。这些站点通常又需要会话保持或七层处理。HAProxy运行在时下的硬件上,完全可以支持数以万计的并发连接。并且它的运行模式使得它可以很简单安全的整合进你当前的架构中,同时可以保护你的web服务器不被暴露到网络上。
HAProxy借助于OS上几种常见的技术来实现性能的最大化:
- 单进程、事件驱动模型显著降低了上下文切换的开销及内存占用
- 在任何可用的情况下,单缓冲(single buffering)机制能以不复制任何数据的方式完成读写操作,这会节约大量的CPU时钟周期及内存带宽
- 借助于Linux 2.6(>= 2.6.27.19)上的
splice()
系统调用,HAProxy可以实现零复制转发(Zero-copy forwarding),在Linux 3.5及以上的OS中还可以实现零复制启动(zero-starting) - 内存分配器在固定大小的内存池中可实现即时内存分配,这能够显著减少创建一个会话的时长
- 树型存储:侧重于使用作者多年前开发的弹性二叉树, 实现了以O(log(N))的低开销来保持计时器命令、保持运行队列命令及管理轮询及最少连接队列
了解了HAProxy后,接下来我们在192.168.243.167和192.168.243.168这两台机器上,分别搭建HAProxy节点。两台机器的搭建步骤都是一样的,这里以haproxy01
为例。首先,安装一些工具:
[root@haproxy01 ~]# yum install -y gcc wget
然后到官网上复制源码包的下载链接,使用wget
命令进行下载:
[root@haproxy01 ~]# cd /usr/local/src
[root@haproxy01 /usr/local/src]# wget http://www.haproxy.org/download/2.3/src/haproxy-2.3.1.tar.gz
解压下载好的源码包:
[root@haproxy01 /usr/local/src]# tar -zxvf haproxy-2.3.1.tar.gz
[root@haproxy01 /usr/local/src]# cd haproxy-2.3.1
[root@haproxy01 /usr/local/src/haproxy-2.3.1]# ls
BRANCHES CHANGELOG contrib CONTRIBUTING doc examples include INSTALL LICENSE MAINTAINERS Makefile README reg-tests ROADMAP scripts src SUBVERS tests VERDATE VERSION
[root@haproxy01 /usr/local/src/haproxy-2.3.1]#
执行如下命令进行编译安装:
[root@haproxy01 /usr/local/src/haproxy-2.3.1]# make TARGET=linux31 PREFIX=/usr/local/haproxy
[root@haproxy01 /usr/local/src/haproxy-2.3.1]# make install PREFIX=/usr/local/haproxy
[root@haproxy01 /usr/local/src/haproxy-2.3.1]# mkdir /etc/haproxy # 创建配置文件存储目录
创建HAProxy的用户组和用户:
[root@haproxy01 ~]# groupadd -r -g 149 haproxy
[root@haproxy01 ~]# useradd -g haproxy -r -s /sbin/nologin -u 149 haproxy
创建HAProxy配置文件:
[root@haproxy01 ~]# vim /etc/haproxy/haproxy.cfg
#logging options
global
log 127.0.0.1 local0 info
maxconn 5120
chroot /usr/local/haproxy
uid 99
gid 99
daemon
quiet
nbproc 20
pidfile /var/run/haproxy.pid
defaults
log global
#使用4层代理模式,”mode http”为7层代理模式
mode tcp
#if you set mode to tcp,then you nust change tcplog into httplog
option tcplog
option dontlognull
retries 3
option redispatch
maxconn 2000
timeout connect 5s
#客户端空闲超时时间为 60秒 则HA 发起重连机制
timeout client 60s
#服务器端链接超时时间为 15秒 则HA 发起重连机制
timeout server 15s
#front-end IP for consumers and producters
listen rabbitmq_cluster
bind 0.0.0.0:5672
#配置TCP模式
mode tcp
#balance url_param userid
#balance url_param session_id check_post 64
#balance hdr(User-Agent)
#balance hdr(host)
#balance hdr(Host) use_domain_only
#balance rdp-cookie
#balance leastconn
#balance source //ip
#简单的轮询
balance roundrobin
#rabbitmq集群节点配置 #inter 每隔五秒对mq集群做健康检查,2次正确证明服务器可用,2次失败证明服务器不可用,并且配置主备机制
server rabbitmq01 192.168.243.164:5672 check inter 5000 rise 2 fall 2
server rabbitmq02 192.168.243.165:5672 check inter 5000 rise 2 fall 2
server rabbitmq03 192.168.243.166:5672 check inter 5000 rise 2 fall 2
#配置haproxy web监控,查看统计信息
listen stats
bind 192.168.243.167:8100
mode http
option httplog
stats enable
#设置haproxy监控地址为http://localhost:8100/rabbitmq-stats
stats uri /rabbitmq-stats
stats refresh 5s
启动haproxy:
[root@haproxy01 ~]# /usr/local/haproxy/sbin/haproxy -f /etc/haproxy/haproxy.cfg
检查haproxy进程是否正常:
[root@haproxy01 ~]# netstat -lntp |grep 8100
tcp 0 0 192.168.243.167:8100 0.0.0.0:* LISTEN 3414/haproxy
[root@haproxy01 ~]#
浏览器访问http://192.168.243.167:8100/rabbitmq-stats,可以在该页面查看一些监控统计信息:
RabbitMQ集群整合高可用组件KeepAlived
使用HAProxy实现了RabbitMQ集群的负载均衡后,应用端就可以通过HAProxy来访问RabbitMQ集群,但是HAProxy自身还是存在单点问题,HAProxy挂掉就无法访问其后端的RabbitMQ集群了。因此,我们需要结合KeepAlived组件实现两个HAProxy节点的主备切换,达到高可用的目的。
KeepAlived软件主要是通过VRRP协议实现高可用功能的。VRRP是Virtual Router RedundancyProtocol(虚拟路由器冗余协议)的缩写,VRRP出现的目的就是为了解决静态路由单点故障问题,VRRP是通过一种竞选机制来将路由的任务交给某台VRRP路由器的。它能够保证当个别节点宕机时,整个网络可以不间断地运行,所以Keepalived一方面具有配置管理LVS的功能,同时还具有对LVS下面节点进行健康检查的功能,另一方面也可实现系统网络服务的高可用功能。
KeepAlived服务的三个重要功能:
- 管理LVS负载均衡软件
- 实现LVS集群节点的健康检查
- 作为系统网络服务的高可用性(failover)
Keepalived高可用服务对节点之间的故障切换转移,是通过VRRP来实现的。在Keepalived服务正常工作时,主Master节点会不断地向备节点发送(多播的方式)心跳消息,用以告诉备Backup节点自己还活看,当主Master节点发生故障时,就无法发送心跳消息,备节点也就因此无法继续检测到来自主Master节点的心跳了,于是调用自身的接管程序,接管主Master节点的IP资源及服务。而当主Master节点恢复时备Backup节点又会释放主节点故障时自身接管的IP资源及服务,恢复到原来的备用角色。
接下来我们开始安装KeepAlived,同样的,我们需在192.168.243.167和192.168.243.168这两台机器上,分别安装KeepAlived。两台机器的安装步骤都是一样的,这里以haproxy01
为例。
首先,安装一些编译时所依赖的工具包:
[root@haproxy01 ~]# yum install -y openssl openssl-devel psmisc
然后,到官网下载地址上复制下载链接:
然后使用wget
命令进行下载:
[root@haproxy01 ~]# cd /usr/local/src
[root@haproxy01 /usr/local/src]# wget https://www.keepalived.org/software/keepalived-2.1.5.tar.gz
解压源码包并编译安装:
[root@haproxy01 /usr/local/src]# tar -zxvf keepalived-2.1.5.tar.gz
[root@haproxy01 /usr/local/src]# cd keepalived-2.1.5
[root@haproxy01 /usr/local/src/keepalived-2.1.5]# ./configure --prefix=/usr/local/keepalived
[root@haproxy01 /usr/local/src/keepalived-2.1.5]# make && make install
创建配置文件存放目录,并创建keepalived的配置文件:
[root@haproxy01 ~]# mkdir /etc/keepalived
[root@haproxy01 ~]# vim /etc/keepalived/keepalived.conf
! Configuration File for keepalived
global_defs {
router_id haproxy01 ##标识节点的字符串,通常为hostname
script_user root
enable_script_security
}
vrrp_script chk_haproxy {
script "/etc/keepalived/haproxy_check.sh" ##执行脚本位置
interval 2 ##检测时间间隔
weight -20 ##如果条件成立则权重减20
}
vrrp_instance VI_1 {
state MASTER ## 主节点为MASTER,备份节点为BACKUP
interface ens32 ## 绑定虚拟IP的网络接口(网卡),与本机IP地址所在的网络接口相同(我这里是ens32)
virtual_router_id 167 ## 虚拟路由ID号(主备节点一定要相同)
mcast_src_ip 192.168.243.167 ## 本机ip地址
priority 100 ##优先级配置(0-254的值)
nopreempt
advert_int 1 ## 组播信息发送间隔,俩个节点必须配置一致,默认1s
authentication { ## 认证匹配
auth_type PASS
auth_pass 123456
}
track_script {
chk_haproxy
}
virtual_ipaddress {
192.168.243.100 ## 虚拟ip,可以指定多个
}
}
haproxy02
节点(backup)的keepalived配置文件内容如下:
! Configuration File for keepalived
global_defs {
router_id haproxy02 ##标识节点的字符串,通常为hostname
script_user root
enable_script_security
}
vrrp_script chk_haproxy {
script "/etc/keepalived/haproxy_check.sh" ##执行脚本位置
interval 2 ##检测时间间隔
weight -20 ##如果条件成立则权重减20
}
vrrp_instance VI_1 {
state BACKUP ## 主节点为MASTER,备份节点为BACKUP
interface ens32 ## 绑定虚拟IP的网络接口(网卡),与本机IP地址所在的网络接口相同(我这里是ens32)
virtual_router_id 167 ## 虚拟路由ID号(主备节点一定要相同)
mcast_src_ip 192.168.243.168 ## 本机ip地址
priority 90 ##优先级配置(0-254的值)
nopreempt
advert_int 1 ## 组播信息发送间隔,俩个节点必须配置一致,默认1s
authentication { ## 认证匹配
auth_type PASS
auth_pass 123456
}
track_script {
chk_haproxy
}
virtual_ipaddress {
192.168.243.100 ## 虚拟ip,可以指定多个
}
}
编写haproxy的健康检查脚本(两个节点该文件内容一致即可):
[root@haproxy01 ~]# vim /etc/keepalived/haproxy_check.sh
#!/bin/bash
COUNT=`ps -C haproxy --no-header |wc -l`
if [ $COUNT -eq 0 ];then
/usr/local/haproxy/sbin/haproxy -f /etc/haproxy/haproxy.cfg
sleep 2
if [ `ps -C haproxy --no-header |wc -l` -eq 0 ];then
killall keepalived
fi
fi
为检查脚本赋予可执行权限:
[root@haproxy01 ~]# chmod a+x /etc/keepalived/haproxy_check.sh
经过以上步骤在两个节点上均安装好keepalived后,就可以分别在两个节点上使用如下命令启动keepalived了:
$ systemctl start keepalived
检查keepalived的服务状态:
[root@haproxy01 ~]# systemctl status keepalived
● keepalived.service - LVS and VRRP High Availability Monitor
Loaded: loaded (/usr/lib/systemd/system/keepalived.service; disabled; vendor preset: disabled)
Active: active (running) since 六 2020-11-28 17:42:06 CST; 7s ago
Process: 9722 ExecStart=/usr/local/keepalived/sbin/keepalived $KEEPALIVED_OPTIONS (code=exited, status=0/SUCCESS)
CGroup: /system.slice/keepalived.service
├─9723 /usr/local/keepalived/sbin/keepalived -D
└─9724 /usr/local/keepalived/sbin/keepalived -D
11月 28 17:42:06 haproxy01 Keepalived_vrrp[9724]: VRRP_Script(chk_haproxy) succeeded
11月 28 17:42:10 haproxy01 Keepalived_vrrp[9724]: (VI_1) Receive advertisement timeout
11月 28 17:42:10 haproxy01 Keepalived_vrrp[9724]: (VI_1) Entering MASTER STATE
11月 28 17:42:10 haproxy01 Keepalived_vrrp[9724]: (VI_1) setting VIPs.
11月 28 17:42:10 haproxy01 Keepalived_vrrp[9724]: (VI_1) Sending/queueing gratuitous ARPs on ens32 for 192.168.243.100
11月 28 17:42:10 haproxy01 Keepalived_vrrp[9724]: Sending gratuitous ARP on ens32 for 192.168.243.100
11月 28 17:42:10 haproxy01 Keepalived_vrrp[9724]: Sending gratuitous ARP on ens32 for 192.168.243.100
11月 28 17:42:10 haproxy01 Keepalived_vrrp[9724]: Sending gratuitous ARP on ens32 for 192.168.243.100
11月 28 17:42:10 haproxy01 Keepalived_vrrp[9724]: Sending gratuitous ARP on ens32 for 192.168.243.100
11月 28 17:42:10 haproxy01 Keepalived_vrrp[9724]: Sending gratuitous ARP on ens32 for 192.168.243.100
[root@haproxy01 ~]#
高可用测试
安装好Keepalived之后,此时可以看到VIP在haproxy01
这个节点上:
[root@haproxy01 ~]# ip a |grep 192.168.243.100
inet 192.168.243.100/32 scope global ens32
[root@haproxy01 ~]#
我们来模拟下节点宕机,看看VIP能否正常漂移到haproxy02
节点上。将Keepalived服务给停掉:
[root@haproxy01 ~]# systemctl stop keepalived
[root@haproxy01 ~]# killall keepalived
此时haproxy01
节点上已经没有绑定VIP了:
[root@haproxy01 ~]# ip a |grep 192.168.243.100
[root@haproxy01 ~]#
正常情况下VIP会漂移到haproxy02
节点上,使得该节点的haproxy可以继续对外提供服务,实现双机热备的高可用模式:
[root@haproxy02 ~]# ip a |grep 192.168.243.100
inet 192.168.243.100/32 scope global ens32
[root@haproxy02 ~]#
RabbitMQ集群恢复与故障转移的5种解决方案
本小节简单介绍下常见的RabbitMQ镜像队列集群故障恢复的解决方案和应用场景,假设我们现在有两个节点A和B组成的一个镜像队列集群,其中B是Master,A是Slave。
场景1:A先停,B后停
方案1:该场景下B是Master,只要先启动B,再启动A即可。或者先启动A,在30秒之内启动B即可恢复镜像队列
场景2:A、B同时停机
方案2:该场景可能是由于机房掉电等原因造成的,只需在30秒之内连续启动A和B即可恢复镜像
场景3:A先停,B后停,且A无法恢复
方案3:该场景是1场景的加强版,因为B是Master,所以等B起来以后,在B节点上调用控制台命令:rabbitmqctl forget_cluster_node A
解除与A的Cluster关系,再将新的Slave节点加入B即可重新恢复镜像队列
场景4:A先停,B后停,且B无法恢复
方案4:该场景是场景3的加强版,比较难处理,原因是因为Master节点无法恢复。在3.1.x时代之前没有什么好的解决方案,因为B是主节点,所以直接启动A是不行的。当A无法启动的时候,也就没办法在A节点上调用之前的rabbitmqctl forget_cluster_node B
命令了。但是现在已经有解决方案了,在3.4.2以后的版本中,forget_cluster_node
支持--offline
参数。这就意味着允许rabbitmqctl
在理想节点上执行该命令,迫使RabbitMQ在未启动Slave节点中选择一个节点作为Master。当在A节点执行rabbitmqctl forget_cluster_node --offline B
时,RabbitMQ会mock一个节点代表A,执行forget_cluster_node
命令将B踢出Cluster,然后A就可以正常启动了,最后将新的Slave节点加入集群即可重新恢复镜像队列
场景5:A先停、B后停, 且A、B均无法恢复,但是能得到A或B的磁盘文件
方案5:这种场景更加难处理,只能通过恢复数据的方式去尝试恢复,将A或B的数据库文件(默认在$RABBIT_HOME/var/lib/
目录中)拷贝到新节点对应的目录下,再将新节点的hostname
改成A或B的hostname
,如果是A节点(Slave)的磁盘文件,则按照场景4处理即可,如果是B节点(Master)的磁盘文件,则按照场景3处理,最后将新的Slave加入到新节点后完成恢复
RabbitMQ集群延迟队列插件应用
延迟插件的作用:
- 延迟插件可以实现消息的延迟推送、定时任务(消息)的执行。包括一些消息重试策略的配合使用,以及用于业务削峰限流、降级的异步延迟消息机制,都是延迟队列的实际应用场景
我们来安装一下这个插件,该插件可以在Github仓库上下载到:
我们也可以到官网上获取官方提供的一些插件:
将下载好的插件上传到服务器上:
[root@rabbitmq01 ~]# ls
rabbitmq_delayed_message_exchange-3.8.9-0199d11c.ez
[root@rabbitmq01 ~]#
- Tips:注意插件版本尽量要与RabbitMQ的版本一致,避免版本不兼容导致的问题
将插件包拷贝到RabbitMQ的插件目录下:
[root@rabbitmq01 ~]# cp rabbitmq_delayed_message_exchange-3.8.9-0199d11c.ez /usr/lib/rabbitmq/lib/rabbitmq_server-3.8.9/plugins/
集群模式下还需要将该插件包分发给其他RabbitMQ节点,也是放到插件目录下:
[root@rabbitmq01 ~]# scp rabbitmq_delayed_message_exchange-3.8.9-0199d11c.ez rabbitmq02:/usr/lib/rabbitmq/lib/rabbitmq_server-3.8.9/plugins/
[root@rabbitmq01 ~]# scp rabbitmq_delayed_message_exchange-3.8.9-0199d11c.ez rabbitmq03:/usr/lib/rabbitmq/lib/rabbitmq_server-3.8.9/plugins/
在集群的各个节点中执行如下命令启用该插件:
$ rabbitmq-plugins enable rabbitmq_delayed_message_exchange
插件启用成功后,我们到管控台上新建一个延迟消息类型的Exchange:
然后创建一个队列:
将该队列绑定到Exchange上:
尝试发送一条延迟消息:
此时到队列界面上,可以看到队列中并没有任何消息:
等待15秒后,队列中就有一条消息了:
查看消息的内容如下:
能达到这个延迟投递的效果,代表我们的延迟消息插件安装成功,并且能够正常投递延迟消息了。该插件在实际开发中,应用得非常广泛,有兴趣的小伙伴可以继续深入了解下。