目录
1、简介
1.1、集群模式
1.2、节点类型
二、实战
2.1、普通集群搭建
2.2、镜像集群
2.3、负载均衡
1、简介
RabbitMQ基于Erlang编写,Erlang天然支持分布式,只需要通过同步Erlang集群各节点的erlang.cookie实现。
但是本身并不支持负载均衡。
1.1、集群模式
集群模式有两种:普通集群模式、镜像集群模式。
①普通集群模式
只进行元数据信息的同步:
- 队列元数据:队列名称和属性;
- 交换器元数据:交换器名称、类型和属性;
- 绑定元数据:一张简单的表格展示了如何将消息路由到队列;
- vhost元数据:为vhost内的队列、交换器和绑定提供命名空间和安全属性;
因此访问任何一个节点查询queue、user、exchange、vhost等信息都是相同的,
需要注意的是,消息的存放只会在创建该消息队列的那个节点上,并不会在每个节点都存储。在非数据所在节点获取数据时会通过元数据信息去路由转发到对应的数据所在节点上去获取。
优点:消息存储可以平摊到各个节点,提高集群的消息积压能力;消息不需要同步复制存储到所有节点,效率高。
缺点:不能保证队列的高可用性,当节点宕机会导致该节点队列无法应用,只能重启。
②镜像集群模式
采用镜像队列的方式保证队列的可靠性,将队列镜像到集群中的其它节点,当集群中一个节点失效了,队列就能自动的切换到镜像中的节点,一个镜像队列中包含有1个master和若干个slave。其主从节点包含如下几个特点:
- 消息的读写都是在master进行,并不是读写分离;
- master接收命令后会向slave进行组播,slave会命令执行顺序执行;
- master失效,根据节点加入的时间,最先加入的slave会被提升为master;
- 互为镜像的是队列,并非节点,集群中不同节点可以互为镜像队列,也就是说队列的master可以分布在不同的节点上。
与普通集群的区别主要是具有镜像队列,可以将消息实体存储到各个节点镜像队列中。
优点:高可用。
缺点:与普通集群相比,集群消息积压能力减弱,还多了消息数据在各节点的传输步骤,性能降低。
集群模式的选择应该根据业务场景取舍。
1.2、节点类型
两种类型:磁盘节点和内存节点。
磁盘节点会将元数据存储在磁盘中,重启后可以读取磁盘进行重建,保证元数据不丢失。
内存节点会将元数据存储在内存中,性能更高,但是重启后元数据就丢失了。
虽然内存节点重启后元数据会丢失,但是可以通过集群里的磁盘节点同步元数据信息(内存节点唯一存储到磁盘的信息就是磁盘节点的地址)。
因此,集群至少需要有一个磁盘节点才能保证可用性,若集群的唯一磁盘节点宕机了,将不能更改任何东西,例如创建队列、交换器、添加或删除节点等;但是还可以继续路由消息(因为其他节点的元数据还存在)。
在集群中,最好设置有两个及以上磁盘节点。
二、实战
RabbitMQ版本: rabbitmq:3-management
docker版本:19.03.8
3台虚拟机centos7服务器ip:192.168.45.129、192.168.45.130、192.168.45.131
我们搭建一个2台磁盘节点(129,131)、一台内存节点的集群(130)。
2.1、普通集群搭建
三台服务器都进行以下两个操作:
1、 拉取镜像
docker pull rabbitmq:3-management
2、配置挂载目录
mkdir -p /home/soft/rabbitmq/data
服务器129执行
docker run -d --restart=always \
--hostname myrabbit01 --name rabbit01 \
-e RABBITMQ_DEFAULT_USER=user -e RABBITMQ_DEFAULT_PASS=password \
-e RABBITMQ_ERLANG_COOKIE='rabbitmqCookie' \
-p 15672:15672 -p 5672:5672 -p 5671:5671 -p 4369:4369 -p 25672:25672 \
-v /home/soft/rabbitmq/data:/var/lib/rabbitmq \
--add-host myrabbit02:192.168.45.130 \
--add-host myrabbit03:192.168.45.131 \
rabbitmq:3-management
服务器130执行
docker run -d --restart=always \
--hostname myrabbit02 --name rabbit02 \
-e RABBITMQ_DEFAULT_USER=user -e RABBITMQ_DEFAULT_PASS=password \
-e RABBITMQ_ERLANG_COOKIE='rabbitmqCookie' \
-p 15672:15672 -p 5672:5672 -p 5671:5671 -p 4369:4369 -p 25672:25672 \
-v /home/soft/rabbitmq/data:/var/lib/rabbitmq \
--add-host myrabbit01:192.168.45.129 \
--add-host myrabbit03:192.168.45.131 \
rabbitmq:3-management
服务器131执行
docker run -d --restart=always \
--hostname myrabbit03 --name rabbit03 \
-e RABBITMQ_DEFAULT_USER=user -e RABBITMQ_DEFAULT_PASS=password \
-e RABBITMQ_ERLANG_COOKIE='rabbitmqCookie' \
-p 15672:15672 -p 5672:5672 -p 5671:5671 -p 4369:4369 -p 25672:25672 \
-v /home/soft/rabbitmq/data:/var/lib/rabbitmq \
--add-host myrabbit01:192.168.45.129 \
--add-host myrabbit02:192.168.45.130 \
rabbitmq:3-management
执行语句分析:
-d 后台执行 --restart=always 容器自动启动
--hostname myrabbit03 主机名 --name rabbit03 容器名
-e RABBITMQ_DEFAULT_USER=user 管理界面账号 -e RABBITMQ_DEFAULT_PASS=password 管理界面密码
-e RABBITMQ_ERLANG_COOKIE='rabbitmqCookie' 集群cookie,同一集群的节点设置cookie一致
-p 15672:15672 管理界面端口 -p 5672:5672 -p 5671:5671 连接端口
-p 4369:4369 -p 25672:25672 集群所需端口
-v /home/soft/rabbitmq/data:/var/lib/rabbitmq 挂载数据目录
--add-host myrabbit01:192.168.45.129 \ 添加host
--add-host myrabbit02:192.168.45.130 \
rabbitmq:3-management
执行完访问129管理界面可以看到并没有其他节点的信息。
我们接着操作129节点去加入131节点
①进入容器
docker exec -it rabbit01 bash
②关闭应用
rabbitmqctl stop_app
③重置节点(数据都会清除,按需使用,新搭建的节点可以忽略此步骤)
rabbitmqctl reset
④加入131节点,--ram的意思是将rabbit01设置为内存节点,不配置的话默认是磁盘节点。
rabbitmqctl join_cluster --ram rabbit@myrabbit03
⑤启动应用
rabbitmqctl start_app
⑥退出容器
exit
这时候我们再去看管理界面
可以看到,已经有rabbit03节点的信息,且rabbit01是RAM节点,即内存节点。
我们继续操作130节点加入131节点,并且设置为磁盘节点。
如此,普通集群就搭建好了。
测试的话可以通过在130管理界面创建队列和新增消息,然后在129管理界面获取消息,看看能不能获取消息。
并且在130创建队列和非持久化消息后,我们通过stop_app关闭应用后可以在管理界面看到该队列变成down状态,用start_app再次启动后会发现消息丢失了。
这里就不演示测试步骤了。
2.2、镜像集群
在搭建的普通集群基础上操作129节点。
①进入容器
docker exec -it rabbit01 bash
②设置Policies策略,实现镜像队列。ha-all是自定义策略名称,^是队列名称的匹配(正则表达式)表示所有队列,all表示复制到所有节点。在集群某个节点设置策略,会自动同步到集群所有节点。 也可以通过管理界面设置策略,这里不详述。
镜像队列详解参考
rabbitmqctl set_policy ha-all "^" '{"ha-mode":"all"}'
如此便完成了。
我们在130创建队列和非持久化消息后,通过stop_app关闭应用后可以在管理界面看到该队列没有变成down状态,依然可以正常工作,消息依然存在。
2.3、负载均衡
前面我们提到RabbitMQ天然支持分布式,但是不支持负载均衡。
我们可以通过nginx实现负载均衡。docker安装nginx
我们在129服务器安装nginx,运行容器指令如下,映射15676端口作为mq管理端接口,5676端口作为mq的tcp端口(这样访问服务器的15676和5676端口将映射到nginx容器的15676和5676端口,而这两个端口又被代理转发到我们的mq集群)。
docker run -p 8080:80 -p 5676:5676 -p 15676:15676 --restart always --name mynginx -v /home/soft/nginx/:/etc/nginx/ -d nginx
修改nginx容器挂载目录的nginx.conf配置文件
vi /home/soft/nginx/nginx.conf
内容如下:
user nginx;
worker_processes 1;
error_log /var/log/nginx/error.log warn;
pid /var/run/nginx.pid;
events {
worker_connections 1024;
}
http {
include /etc/nginx/mime.types;
default_type application/octet-stream;
log_format main '$remote_addr - $remote_user [$time_local] "$request" '
'$status $body_bytes_sent "$http_referer" '
'"$http_user_agent" "$http_x_forwarded_for"';
access_log /var/log/nginx/access.log main;
sendfile on;
#tcp_nopush on;
keepalive_timeout 65;
#gzip on;
#mq管理界面负载均衡配置
upstream mqweb{
server 192.168.45.129:15672;
server 192.168.45.130:15672;
server 192.168.45.131:15672;
}
server{
listen 15676;
server_name 192.168.45.129;
location / {
proxy_pass http://mqweb;
}
}
include /etc/nginx/conf.d/*.conf;
}
#mq tcp连接负载均衡配置。注意!!!tcp配置需要在stream模块内,跟http模块同级,不在http模块里边,且不需要server_name和location
stream{
upstream mqtcp{
server 192.168.45.129:5672 max_fails=2 fail_timeout=5s;
server 192.168.45.130:5672 max_fails=2 fail_timeout=5s;
server 192.168.45.131:5672 max_fails=2 fail_timeout=5s;
}
server{
listen 5676;
proxy_pass mqtcp;
}
}
测试的话访问http://192.168.45.129:15676/,可以访问到mq管理界面则说明成功。
可以新建一个springboot项目简单的测试一下连接5676端口是否可以发送消息啥的,这里就不演示了。