RabbitMQ + 镜像队列 + HAProxy 实现负载均衡的集群



一、集群管理(RabbitMQ扩容)

1. 环境介绍

hostname

ip

mq1

192.168.80.16

mq2

192.168.80.17

mq3

192.168.80.18

mqhaproxy

192.168.80.19

单机部署

单机版安装地址:RabbitMQ3.8.4安装和配置

rabbitmq镜像集群有主备吗 rabbitmq配置镜像队列_linux


注意:修改三台节点的hostname文件,设置主机名称,分别mq1/mq2/mq3

rabbitmq镜像集群有主备吗 rabbitmq配置镜像队列_分布式_02

修改三台节点的/etc/hosts文件,设置节点名称和ip地址的映射关系

rabbitmq镜像集群有主备吗 rabbitmq配置镜像队列_分布式_03



2. 从mq1拷贝.erlang.cookie到mq2、mq3的相应目录(元数据共享)

如果没有该文件,手动创建 /var/lib/rabbitmq/.erlang.cookie ,生成Cookie字符串,或者启动一次RabbitMQ自动生成该文件。生产中推荐使用第三方工具生成。

2.1 我们首先在mq1上启动单机版RabbitMQ,以生成Cookie文件:
systemctl start rabbitmq-server

rabbitmq镜像集群有主备吗 rabbitmq配置镜像队列_分布式_04

2.2 开始准备同步 .erlang.cookie 文件
RabbitMQ的集群依赖Erlang的分布式特性,需要保持Erlang Cookie一致才能实现集群节点的认证和通信,我们直接使用scp命令从node1远程传输。
scp /var/lib/rabbitmq/.erlang.cookie root@node3:/var/lib/rabbitmq/
	scp /var/lib/rabbitmq/.erlang.cookie root@node4:/var/lib/rabbitmq/

rabbitmq镜像集群有主备吗 rabbitmq配置镜像队列_rabbitmq镜像集群有主备吗_05


同步完 .erlang.cookie 文件 后,在当前节点就可以访问其他节点

2.3 修改mq2和mq3上该文件的所有者为:rabbitmq:rabbitmq
chown rabbitmq:rabbitmq /var/lib/rabbitmq/.erlang.cookie

rabbitmq镜像集群有主备吗 rabbitmq配置镜像队列_分布式_06


注意: .erlang.cookie文件权限为400


3. 使用下述命令启动mq2和mq3上的RabbitMQ:

systemctl start rabbitmq-server

rabbitmq镜像集群有主备吗 rabbitmq配置镜像队列_haproxy_07


4. 将mq2和mq3这两个节点加入到集群中(默认情况下:一个节点就是一个集群)

4.1 以mq1节点为集群,查看集群状态:此时运行节点只有mq1节点
rabbitmqctl cluster_status

rabbitmq镜像集群有主备吗 rabbitmq配置镜像队列_分布式_08

4.2 停止Erlang VM上运行的RabbitMQ应用,保持Erlang VM的运行
rabbitmqctl stop_app

rabbitmq镜像集群有主备吗 rabbitmq配置镜像队列_分布式_09

4.3 移除当前RabbitMQ虚拟主机中的所有数据:重置
rabbitmqctl reset

rabbitmq镜像集群有主备吗 rabbitmq配置镜像队列_rabbitmq_10

4.4 将当前RabbitMQ的主机加入到rabbit@node2这个虚拟主机的集群中。一个节点也是集群。
rabbitmqctl join_cluster rabbit@mq1

rabbitmq镜像集群有主备吗 rabbitmq配置镜像队列_rabbitmq_11

4.5 启动当前Erlang VM上的RabbitMQ应用
rabbitmqctl start_app

rabbitmq镜像集群有主备吗 rabbitmq配置镜像队列_haproxy_12

4.6 再次mq1节点集群状态,此时多出一个节点mq2
rabbitmqctl cluster_status

rabbitmq镜像集群有主备吗 rabbitmq配置镜像队列_linux_13

4.7 使用相同的方式,将mq3也加入到当前集群中

rabbitmq镜像集群有主备吗 rabbitmq配置镜像队列_分布式_14

4.8 再次mq1节点集群状态,此时多出一个节点mq2
rabbitmqctl cluster_status

rabbitmq镜像集群有主备吗 rabbitmq配置镜像队列_linux_15

注意:
1. rabbit@node2 表示RabbitMQ节点名称,默认前缀就是 rabbit , @ 之后是当前虚拟主机所在的物理主机 hostname 。
	2. 注意检查下hostname要可以相互ping通
	3. join_cluster默认是使用disk模式,后面可以加入参数--ram启用内存模式



5. 移出集群节点使用:

# 将虚拟主机(RabbitMQ的节点)rabbit@node3从集群中移除,但是rabbit@node3还保留集群信息
# 还是会尝试加入集群,但是会被拒绝。可以重置rabbit@node3节点。
	rabbitmqctl forget_cluster_node rabbit@mq2

rabbitmq镜像集群有主备吗 rabbitmq配置镜像队列_分布式_16


重启节点报错

rabbitmq镜像集群有主备吗 rabbitmq配置镜像队列_haproxy_17


重置后,重新启动

rabbitmq镜像集群有主备吗 rabbitmq配置镜像队列_rabbitmq镜像集群有主备吗_18

查看集群状态:只有其本身(可以再次将此节点加入现有集群)

rabbitmq镜像集群有主备吗 rabbitmq配置镜像队列_rabbitmq_19

6. 其他操作命令:

#修改集群名称(任意节点执行都可以)
	rabbitmqctl set_cluster_name  集群名称   (只要cookie文件一致,就可以进行名称的修改,修改节点不一定要在一个集群中)
#查看集群状态(任意节点执行都可以)
	rabbitmqctl cluster_status
6.1 修改集群名称
rabbitmqctl set_cluster_name  dabingrabbits

rabbitmq镜像集群有主备吗 rabbitmq配置镜像队列_rabbitmq_20

6.2 将mq2重新加回集群

rabbitmq镜像集群有主备吗 rabbitmq配置镜像队列_haproxy_21


7. web控制台

在三个RabbitMQ节点上的任意一个添加用户,设置用户权限,设置用户标签

rabbitmqctl add_user root 123456
	rabbitmqctl set_permissions --vhost "/" root ".*" ".*" ".*"
	rabbitmqctl set_user_tags --vhost "/" root administrator

rabbitmq镜像集群有主备吗 rabbitmq配置镜像队列_linux_22

可以到web控制台查看集群信息

如果要看到所有RabbitMQ节点上的运行情况,所有节点都需要启用rabbitmq_management 插件

rabbitmq镜像集群有主备吗 rabbitmq配置镜像队列_rabbitmq_23


二、RabbitMQ镜像集群配置(消息分片存储,单点故障)

RabbitMQ中队列的内容是保存在单个节点本地的(声明队列的节点)。跟交换器和绑定不同,它们是对于集群中所有节点的。

如此,则队列内容存在单点故障,解决方式之一就是使用镜像队列。在多个节点上拷贝队列的副本。

每个镜像队列包含一个master,若干个镜像。
	master存在于称为master的节点上。
	所有的操作都是首先对master执行,之后广播到镜像。
	这涉及排队发布,向消费者传递消息,跟踪来自消费者的确认等。
	
	镜像意味着集群,不应该WAN使用。

发布到队列的消息会拷贝到该队列所有的镜像。消费者连接到master,当消费者对消息确认之后,镜像删除master确认的消息。

队列的镜像提供了高可用,但是没有负载均衡。

HTTP API和CLI工具中队列对象的字段原来使用的是slave代表secondaries,现在盖字段的存在仅是为了向后兼容,后续版本会移除。

可以使用策略随时更改队列的类型,可以首先创建一个非镜像队列,然后使用策略将其配置为镜像队列或者反过来。非镜像队列没有额外的基础设施,因此可以提供更高的吞吐率。

master选举策略:

1. 最长的运行镜像    升级为主镜像,前提是假定它与主镜像完全同步。如果没有与主服务器同步的镜像,则仅存在于主服务器上的消息将丢失。
	2. 镜像认为所有以前的消费者都已突然断开连接。它重新排队已传递给客户端但正在等待确认的所有消息。这包括客户端已为其发出确认的消息,例如,确认是在到达节点托管队列主节点之前在线路上丢失了,还是在从主节点广播到镜像时丢失了。在这两种情况下,新的主服务器都别无选择,只能重新排队它尚未收到确认的所有消息。
	3. 队列故障转移时请求通知的消费者将收到取消通知。当镜像队列发生了master的故障转移,系统就不知道向哪些消费者发送了哪些消息。已经发送的等待确认的消息会重新排队
	4. 重新排队的结果是,从队列重新使用的客户端必须意识到,他们很可能随后会收到已经收到的消息。
	5. 当所选镜像成为主镜像时,在此期间发布到镜像队列的消息将不会丢失(除非在提升的节点上发生后续故障)。发布到承载队列镜像的节点的消息将路由到队列主服务器,然后复制到所有镜像。如果主服务器发生故障,则消息将继续发送到镜像,并在完成向主服务器的镜像升级后将其添加到队列中。
	6. 即使主服务器(或任何镜像)在正在发布的消息与发布者收到的确认之间失败,由客户端使用发布者确认发布的消息仍将得到确认。从发布者的角度来看,发布到镜像队列与发布到非镜像队列没有什么不同。

给队列添加镜像要慎重。

ha-mode

ha-params

结果

exactly

count

设置集群中队列副本的个数(镜像+master)。1表示一个副本;也就是master。如果master不可用,行为依赖于队列的持久化机制。2表示1个master和1个镜像。如果master不可用,则根据镜像推举策略从镜像中选出一个做master。如果节点数量比镜像副本个数少,则镜像覆盖到所有节点。如果count个数少于集群节点个数,则在一个镜像宕机后,会在其他节点创建出来一个镜像。将“exactly”模式与“ha-promote-on-shutdown”: “ always”一起使用可能很危险,因为队列可以在整个集群中迁移并在关闭时变得不同步。

all

(none)

镜像覆盖到集群中的所有节点。当添加一个新的节点,队列就会复制过去。这个配置很保守。一般推荐N/2+1个节点。在集群所有节点拷贝镜像会给集群所有节点施加额外的负载,包括网络IO,磁盘IO和磁盘空间使用。

nodes

node

names

在指定node name的节点上复制镜像。node name就是在rabbitmqctlcluster_status命令输出中的node name。如果有不属于集群的节点名称,它不报错。如果指定的节点都不在线,则仅在客户端连接到的声明镜像的节点上创建镜像。

接下来,启用镜像队列:

# 对/节点配置镜像队列,使用全局复制
	rabbitmqctl set_policy ha-all "^" '{"ha-mode":"all"}'

# 配置过半(N/2 + 1)复制镜像队列
	rabbitmqctl set_policy ha-halfmore "queueA" '{"ha-mode":"exactly", "ha-params":2}'

# 指定优先级,数字越大,优先级越高
	rabbitmqctl set_policy --priority 1 ha-all "^" '{"ha-mode":"all"}'
2.1 创建一个测试队列

rabbitmq镜像集群有主备吗 rabbitmq配置镜像队列_rabbitmq_24

2.2 配置队列为 过半(N/2 + 1)复制镜像队列(可以使用正则匹配队列名称,2表示一主一从)
rabbitmqctl  set_policy  ha_test  "^queue.*test$"   '{"ha-mode":"exactly", "ha-params":2}'

rabbitmq镜像集群有主备吗 rabbitmq配置镜像队列_分布式_25


rabbitmq镜像集群有主备吗 rabbitmq配置镜像队列_分布式_26


rabbitmq镜像集群有主备吗 rabbitmq配置镜像队列_linux_27

2.3 关闭镜像mirrors所在的节点mq3

rabbitmq镜像集群有主备吗 rabbitmq配置镜像队列_haproxy_28

2.4 查看队列信息

rabbitmq镜像集群有主备吗 rabbitmq配置镜像队列_linux_29

2.5 将队列设置成非镜像队列

rabbitmq镜像集群有主备吗 rabbitmq配置镜像队列_rabbitmq镜像集群有主备吗_30

2.6 在任意一个节点上面执行即可。默认是将所有的队列都设置为镜像队列,在消息会在不同节点之间复制,各节点的状态保持一致。

rabbitmq镜像集群有主备吗 rabbitmq配置镜像队列_分布式_31


点击上图中的queueA,查看该队列的细节:

rabbitmq镜像集群有主备吗 rabbitmq配置镜像队列_分布式_32

其他细节可以查看官方文档:
https://www.rabbitmq.com/ha.html

三、负载均衡-HAProxy

将客户端的连接和操作的压力分散到集群中的不同节点,防止单个或几台服务器压力过大成为访问的瓶颈,甚至宕机。

HAProxy是一款开源免费,并提供高可用性、负载均衡以及基于TCP和HTTP协议的代理软件,可以支持四层、七层负载均衡,经过测试单节点可以支持10W左右并发连接。

LVS是工作在内核模式(IPVS),支持四层负载均衡,实测可以支撑百万并发连接。

Nginx支持七层的负载均衡(后期的版本也支持四层了),是一款高性能的反向代理软件和Web服务器,可以支持单机3W以上的并发连接。

这里我们使用HAProxy来做RabbitMQ的负载均衡,通过暴露VIP给上游的应用程序直接连接,上游应用程序不感知底层的RabbitMQ的实例节点信息。

3.1 环境准备

hostname

ip

mqhaproxy

192.168.80.19

3.2 安装方式一:rpm安装

wget https://www.haproxy.org/download/2.0/src/haproxy-2.0.1.tar.gz
	
	tar -zxf haproxy-2.1.0.tar.gz
	
	yum install gcc -y
	
	cd haproxy-2.1.0
	make TARGET=linux-glibc
	make install
	
	mkdir /etc/haproxy
	#赋权
	groupadd -r -g 149 haproxy
	# 添加用户
	useradd -g haproxy -r -s /sbin/nologin -u 149 haproxy
	#创建haproxy配置文件
	touch /etc/haproxy/haproxy.cfg
下载haproxy安装包

rabbitmq镜像集群有主备吗 rabbitmq配置镜像队列_linux_33

解压haproxy安装包

rabbitmq镜像集群有主备吗 rabbitmq配置镜像队列_分布式_34

安装编译环境gcc,编译安装包

rabbitmq镜像集群有主备吗 rabbitmq配置镜像队列_rabbitmq_35

安装haproxy

rabbitmq镜像集群有主备吗 rabbitmq配置镜像队列_haproxy_36

安装方式二:yum安装(略)

yum -y install haproxy

如何使用yum安装的,那么haproxy默认在/usr/sbin/haproxy,且会自动创建配置文件/etc/haproxy/haproxy.cfg

3.3 添加用户

赋权
groupadd -r -g 149 haproxy
	useradd -g haproxy -r -s /sbin/nologin -u 149 haproxy

rabbitmq镜像集群有主备吗 rabbitmq配置镜像队列_rabbitmq_37

3.4 配置HAProxy

创建配置文件haproxy.cfg ,并修改配置文件haproxy.cfg

mkdir /etc/haproxy
	touch /etc/haproxy/haproxy.cfg
	
	vim /etc/haproxy/haproxy.cfg

配置文件都做了详细注释,参考如下:

global
	 	log 127.0.0.1 local0 info
	  	# 服务器最大并发连接数;如果请求的连接数高于此值,将其放入请求队列,等待其它连接被释放;
	 	maxconn 5120
	 	# chroot /tmp
	  	# 指定用户
	 	uid 149
	  	# 指定组
		gid 149
	  	# 让haproxy以守护进程的方式工作于后台,其等同于“-D”选项的功能
	  	# 当然,也可以在命令行中以“-db”选项将其禁用;
		daemon
	  	# debug参数
	 	quiet
	  	# 指定启动的haproxy进程的个数,只能用于守护进程模式的haproxy;
	  	# 默认只启动一个进程,
	  	# 鉴于调试困难等多方面的原因,在单进程仅能打开少数文件描述符的场景中才使用多进程模式;
	  	# nbproc 20
	 	nbproc 1
	 	pidfile /var/run/haproxy.pid
	defaults
	 	log global
	  	# tcp:实例运行于纯TCP模式,第4层代理模式,在客户端和服务器端之间将建立一个全双工的连接,
	  	# 且不会对7层报文做任何类型的检查;
	  	# 通常用于SSL、SSH、SMTP等应用;
	 	mode tcp
	 	option tcplog
	 	option dontlognull
	 	retries 3
	 	option redispatch
	 	maxconn 2000
	  	# contimeout 5s
	 	timeout connect 5s
	  	# 客户端空闲超时时间为60秒则HA 发起重连机制
	 	timeout client 60000
	  	# 服务器端链接超时时间为15秒则HA 发起重连机制
	 	timeout server 15000
	listen rabbitmq_cluster
	  	# VIP,反向代理到下面定义的三台Real Server
	 	bind 192.168.80.19:5672
	  	#配置TCP模式
	 	mode tcp
	  	#简单的轮询
	 	balance roundrobin
	  	# rabbitmq集群节点配置
	  	# inter 每隔五秒对mq集群做健康检查,2次正确证明服务器可用,2次失败证明服务器不可用,并且配置主备机制
	 	server rabbitmqNode1 192.168.80.16:5672 check inter 5000 rise 2 fall 2
	 	server rabbitmqNode2 192.168.80.17:5672 check inter 5000 rise 2 fall 2
	 	server rabbitmqNode3 192.168.80.18:5672 check inter 5000 rise 2 fall 2
		#配置haproxy web监控,查看统计信息
	listen stats
	 	bind 192.168.80.19:9000
	 	mode http
	 	option httplog
	  	# 启用基于程序编译时默认设置的统计报告
	 	stats enable
	  	#设置haproxy监控地址为http://mq1:9000/rabbitmq-stats
	 	stats uri /rabbitmq-stats
	
		# 每5s刷新一次页面
	 	stats refresh 5s

拷贝上述配置信息到配置文件haproxy.cfg

rabbitmq镜像集群有主备吗 rabbitmq配置镜像队列_rabbitmq_38

3.5 启动HAProxy:

haproxy -f /etc/haproxy/haproxy.cfg

rabbitmq镜像集群有主备吗 rabbitmq配置镜像队列_haproxy_39


检查进程状态,还可以通过访问http://vip:9000/rabbitmq-stats查看状态

rabbitmq镜像集群有主备吗 rabbitmq配置镜像队列_分布式_40

3.6 关闭:

kill -9 <pid>

3.7 接下来,可以在代码中直接测试,代码配置直接连接到HAProxy和监听端口上即可(略)

rabbitmq镜像集群有主备吗 rabbitmq配置镜像队列_haproxy_41

集群的搭建就已经完成了。



四、监控

RabbitMQ自带的(Management插件)管理控制台功能比较丰富,不仅提供了Web UI界面,还暴露了很多HTTP API的能力。其中也具备基本的监控能力。此外,自带的命令行工具(例如:rabbitmqctl )也比较强大。

不过这些工具都不具备告警的能力。在实际的生产环境中,我们需要知道负载情况和运行监控状态(例如:系统资源、消息积压情况、节点健康状态等),而且当发生问题后需要触发告警。像传统的监控平台Nagios、Zabbix等均提供了RabbitMQ相关的插件支持。

另外,当前云原生时代最热门的Prometheus监控平台也提供了rabbitmq_exporter,结合Grafana漂亮美观的dashboard(可以自定义,也可以在仓库选择一些现有的),我们目前公司就是使用Prometheus + Grafana来监控RabbitMQ的,并实现了水位告警通知。


rabbitmq镜像集群有主备吗 rabbitmq配置镜像队列_分布式_42