基于容器化搭建 RabbitMQ 集群
回顾
Docker swarm
基于 Docker Image: rabbitmq:3.11.3-management 部署单节点 RabbitMQ
部署节点
shell# 端口说明,参见 https://www.rabbitmq.com/networking.html#ports docker run -d --name rabbitmq -e RABBITMQ_DEFAULT_VHOST=my_vhost -e RABBITMQ_DEFAULT_USER=user -e RABBITMQ_DEFAULT_PASS=123456 -p5671:5671 -p5672:5672 -p15671:15671 -p15672:15672 rabbitmq:3.11.3-management访问
host:15672使用账号密码登录可看到 RabbitMQ Management 页面
常见 RabbitMQ 分布式模式
- 主备模式
- 通过 haproxy 来配置
- 主节点提供读写功能,当主节点宕机后,从节点升级为主节点,提供服务
- 主从副本集群
- 队列数据只存在 Master 节点
- Slave 节点拥有除了队列数据外与 Master 节点相同的所有数据和状态
- Slave 节点需要连接到 Master 节点实现队列的转发
- 消费者可以连接到 Master 或 Slave 节点
- 只能保证了服务可以用,无法达到高可用,没有故障自动切换功能
- 适合于消息无需持久化的场合。当队列非持久化,且创建该队列的节点宕机,客户端才可以重连集群其他节点,并重新创建队列。若为持久化,只能等故障节点恢复。
- 镜像集群

- 镜像集群基于
主从副本集群,需要先搭建普通集群, 然后才能设置镜像队列 - 队列机制是将队列在三个节点之间设置主从关系,消息会在三个节点之间进行自动同步
- 如果其中一个节点不可用,并不会导致消息丢失或服务不可用的情况,提升 MQ 集群的整体高可用性。
- 多活模式(Federation 模式)
- 复杂的异地模式
- 远程模式(Shovel 模式)
- 简单的异地模式
- 对比
Federation and/or Shovel Clustering 可能会有多个不同宿主的不同逻辑实例(Brokers) 集群来自于一个独立的逻辑实例 实例可以运行在互不兼容的 RabbitMQ 和 Erlang 版本上 结点必须运行在 RabbitMQ 和 Erlang 的兼容版本上 实例可通过不可靠的广域网连接
通过可选 TLS 的 AMQP 0-9-1 协议连接,需要设置合适特用户和权限实例必须通过可靠的局域网连接
节点将通过可选 TLS 的链接使用共享密钥进行彼此的身份验证实例可以以任何设定的拓扑结构进行连接, 链接可是单或双向的 所有节点双向连接到其他全部节点 强调 CAP 理论中的可用性和分区容忍性(AP) 强调 CAP 理论中的一致性和分区容忍性(CP) 实例中某些交换机(exchanges)可能是联合(federated)的,某些可能是本地的 集群中只有一种形式 连接到实例的客户端只能使用那个实例的非独占(non-exclusive)队列(queues) 连接到任何节点的客户端都可以在所有节点上使用非独占队列。
RabbitMQ Cluster
Clustering Guide — RabbitMQ > Cluster Formation and Peer Discovery — RabbitMQ
- 构建集群的方法
- 手动使用
rabbitmqctl - 通过在配置文件中列出群集节点以声明方式
- 以声明方式使用基于 DNS 的发现
- 以声明方式使用AWS (EC2) 实例发现(通过插件)
- 声明式使用Kubernetes 发现(通过插件)
- 以声明方式使用基于 Consul 的发现(通过插件)
- 声明式使用基于 etcd 的发现(通过插件)
- 手动使用
- 注意
- 一个集群中,节点名必须唯一。在不手动指定节点名(环境变量
RABBITMQ_NODENAME设置)时,默认使用前缀(Rabbit)+主机名(hostname)对节点进行命名 - 一个集群中,节点之间通过主机名进行解析 IP,需要提供解析方法:
- DNS
- hosts 文件
- 节点之间通过 Erlang-Cookie 进行认证
- Cookie 存储位置
- RabbitMQ 服务端 Cookie 文件路径:
/var/lib/rabbitmq/.erlang.cookie - RabbitMQ Cli Cookie 文件路径:
$HOME/.erlang.cookie
- RabbitMQ 服务端 Cookie 文件路径:
- Cookie 设置方法
- 先存放 Cookie 文件到服务端路径
/var/lib/rabbitmq/.erlang.cookie,再启动 MQ - Docker Image 环境变量
RABBITMQ_ERLANG_COOKIE(疑似 3.7+已失效,3.11 不可用) - 在 Docker Swarm 中,通过使用
--secret source=my-erlang-cookie,target=/var/lib/rabbitmq/.erlang.cookie参数可以设置 Cookie 信息
- 先存放 Cookie 文件到服务端路径
- Cookie 存储位置
- 一个集群中,节点名必须唯一。在不手动指定节点名(环境变量
手动使用 rabbitmqctl 创建集群
Docker 创建网卡
shelldocker network create --driver bridge --subnet 172.18.0.0/16 --gateway 172.18.0.1 mynetwork准备 Cookie 文件,并设置文件权限为
400shellecho 12345678 > /tmp/.erlang.cookie chmod 400 /tmp/.erlang.cookie启动多个节点,并确保节点名不相同
shelldocker run -d --name rabbitmq_cluster1 --hostname mqcluster1 --network mynetwork --ip 172.18.0.5 -e RABBITMQ_DEFAULT_VHOST=my_vhost -e RABBITMQ_DEFAULT_USER=user -e RABBITMQ_DEFAULT_PASS=123456 -v /tmp/.erlang.cookie:/var/lib/rabbitmq/.erlang.cookie -p5671:5671 -p5672:5672 -p15671:15671 -p15672:15672 rabbitmq:3.11.3-management docker run -d --name rabbitmq_cluster2 --hostname mqcluster2 --network mynetwork --ip 172.18.0.6 -e RABBITMQ_DEFAULT_VHOST=my_vhost -e RABBITMQ_DEFAULT_USER=user -e RABBITMQ_DEFAULT_PASS=123456 -v /tmp/.erlang.cookie:/var/lib/rabbitmq/.erlang.cookie rabbitmq:3.11.3-management docker run -d --name rabbitmq_cluster3 --hostname mqcluster3 --network mynetwork --ip 172.18.0.7 -e RABBITMQ_DEFAULT_VHOST=my_vhost -e RABBITMQ_DEFAULT_USER=user -e RABBITMQ_DEFAULT_PASS=123456 -v /tmp/.erlang.cookie:/var/lib/rabbitmq/.erlang.cookie rabbitmq:3.11.3-management # 查看节点的集群状态 docker exec -it rabbitmq_cluster1 rabbitmqctl cluster_status docker exec -it rabbitmq_cluster2 rabbitmqctl cluster_status docker exec -it rabbitmq_cluster3 rabbitmqctl cluster_status # rabbitmq_cluster1 rabbit@mqcluster1 # rabbitmq_cluster2 rabbit@mqcluster2 # rabbitmq_cluster3 rabbit@mqcluster3确定每个节点的 ip 信息
shelldocker inspect rabbitmq_cluster1 | grep IPAddress docker inspect rabbitmq_cluster2 | grep IPAddress docker inspect rabbitmq_cluster3 | grep IPAddress # rabbit@mqcluster1 172.18.0.5 # rabbit@mqcluster2 172.18.0.6 # rabbit@mqcluster3 172.18.0.7修改
hosts文件(如果是自定义网卡则不需要此步骤)shelldocker exec -it rabbitmq_cluster1 /bin/bash -c "echo 172.18.0.6 mqcluster2 >> /etc/hosts" docker exec -it rabbitmq_cluster1 /bin/bash -c "echo 172.18.0.7 mqcluster3 >> /etc/hosts" docker exec -it rabbitmq_cluster2 /bin/bash -c "echo 172.18.0.5 mqcluster1 >> /etc/hosts" docker exec -it rabbitmq_cluster2 /bin/bash -c "echo 172.18.0.7 mqcluster3 >> /etc/hosts" docker exec -it rabbitmq_cluster3 /bin/bash -c "echo 172.18.0.5 mqcluster1 >> /etc/hosts" docker exec -it rabbitmq_cluster3 /bin/bash -c "echo 172.18.0.6 mqcluster2 >> /etc/hosts"将节点加入集群
shelldocker exec -it rabbitmq_cluster2 rabbitmqctl stop_app # Stopping rabbit application on node rabbit@e8e0dfd712d1 ... docker exec -it rabbitmq_cluster2 rabbitmqctl reset # Resetting node rabbit@e8e0dfd712d1 ... docker exec -it rabbitmq_cluster2 rabbitmqctl join_cluster rabbit@mqcluster1 # Clustering node rabbit@mqcluster2 with rabbit@mqcluster1 docker exec -it rabbitmq_cluster2 rabbitmqctl start_app # Starting node rabbit@mqcluster2 ... # ...完成

删除一个节点
shelldocker exec -it rabbitmq_cluster3 rabbitmqctl stop_app # Stopping rabbit application on node rabbit@mqcluster3 ... docker exec -it rabbitmq_cluster3 rabbitmqctl reset # Resetting node rabbit@mqcluster3 ... docker exec -it rabbitmq_cluster3 rabbitmqctl start_app # Starting node rabbit@mqcluster3 ...创建一个 RAM 节点
RabbitMQ 集群中节点包括内存节点(RAM)、磁盘节点(Disk,消息持久化),集群中至少有一个 Disk 节点。
首次添加
shelldocker exec -it rabbitmq_cluster3 rabbitmqctl stop_app docker exec -it rabbitmq_cluster3 rabbitmqctl reset docker exec -it rabbitmq_cluster3 rabbitmqctl join_cluster --ram rabbit@mqcluster1 docker exec -it rabbitmq_cluster3 rabbitmqctl start_app
修改节点
shelldocker exec -it rabbitmq_cluster2 rabbitmqctl stop_app docker exec -it rabbitmq_cluster2 rabbitmqctl change_cluster_node_type ram # Turning rabbit@mqcluster2 into a ram node docker exec -it rabbitmq_cluster2 rabbitmqctl start_app
当节点掉线

- 队列不可用,数据只在该节点存放,也取不到了

- 队列不可用,数据只在该节点存放,也取不到了
当节点恢复
- 节点队列数据恢复

- 节点队列数据恢复
创建镜像集群
可以使用
rabbitmqctl或management指定策略方式实现使用
rabbitmqctl命令实现持久镜像队列rabbitmqctl(8) — RabbitMQ > Classic Queue Mirroring — RabbitMQ
`rabbitmqctl set_policy [-p vhost] [--priority priority] [--apply-to apply-to] name pattern definition
shell# definition: JSON格式策略定义 # 包括三个部分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 docker exec -it rabbitmq_cluster1 rabbitmqctl set_policy -p my_vhost mypolicy '^' '{"ha-mode":"all","ha-sync-mode":"automatic"}' # Setting policy "mypolicy" for pattern "^" to "{"ha-mode":"all","ha-sync-mode":"automatic"}" with priority "0" for vhost "my_vhost" ...
开启策略结果


当节点掉线

- 队列可用,且所属节点被变更。数据在别的节点也有,依然可以取到

- 队列可用,且所属节点被变更。数据在别的节点也有,依然可以取到
其他两种用来代替持久镜像队列的类型(使用镜像队列时可优先考虑)
- Quorum Queues — RabbitMQ
- 声明时类型选为
Quorum - 不需要设置策略,会自动选举 Leader
- 当节点下线后,原节点被自动更新
- 声明时类型选为
- Streams — RabbitMQ
- 特点
- 持久化
- 不可变数据(append-only)
- 非破坏性消费者语义(non-destructive consumer semantics)
- 使用场合
- 大扇出(Large fan-outs)
- 流将允许任意数量的消费者以非破坏性的方式使用来自同一队列的相同消息,从而不需要绑定多个队列
- 重放/时间旅行(Replay / Time-travelling)
- 流可以重复读取
- 流将允许用户在日志中的任何位置进行连接并从中进行读取
- 吞吐量性能(Throughput Performance)
- 基于日志的消息传递系统的吞吐量
- 流的设计以性能为主要目标
- 大型日志(Large logs)
- 流被设计为以最小的内存开销以有效的方式存储更大量的数据
- 大扇出(Large fan-outs)
- 特点





