集群架构模式
RabbitMQ提供两种主要集群架构模式:普通集群(常规集群)与镜像集群(Mirrored Cluster),两者在数据复制、可用性和性能方面有明显差异。
定义
普通集群:队列元数据(名称、属性、绑定关系)在所有节点间同步,但消息体仅存储在队列所属的主节点上。
镜像集群:队列内容(消息体与状态)在多个节点间完整复制,每个镜像队列有一个Master和若干Slave。
Maven依赖
XML
<dependency>
<groupId>com.rabbitmq</groupId>
<artifactId>amqp-client</artifactId>
<version>5.20.0</version>
</dependency>
普通集群
架构特点
Java
┌─────────┐ ┌─────────┐ ┌─────────┐
│ Node A │────▶│ Node B │────▶│ Node C │
│ Queue1 │ │ Queue2 │ │ Queue3 │
│ (Master)│ │ (Master)│ │ (Master)│
└─────────┘ └─────────┘ └─────────┘
▲ ▲ ▲
│ 元数据同步 │ 元数据同步 │ 元数据同步
└───────────────┴───────────────┘
- 消息体仅存在于Master节点,Slave节点仅存元数据
- 客户端连接任意节点均可访问集群所有队列(自动路由到Master)
- 若Master节点宕机,队列不可用(除非配置队列镜像)
连接示例
Bash
import com.rabbitmq.client.*;
public class ClusterConnection {
private static final String QUEUE_NAME = "cluster_queue";
public static void main(String[] args) throws Exception {
ConnectionFactory factory = new ConnectionFactory();
// 配置多个节点地址,客户端自动尝试连接可用节点
factory.setHost("node-a.rabbitmq.local");
factory.setPort(5672);
Connection connection = factory.newConnection();
Channel channel = connection.createChannel();
// 声明队列(元数据自动同步到集群所有节点)
channel.queueDeclare(QUEUE_NAME, true, false, false, null);
// 发送消息(消息存储在队列所在Master节点)
String message = "cluster message";
channel.basicPublish("", QUEUE_NAME, null, message.getBytes("UTF-8"));
// 消费消息(连接任意节点,自动路由到Master)
DeliverCallback deliverCallback = (consumerTag, delivery) -> {
String msg = new String(delivery.getBody(), "UTF-8");
System.out.println("收到: " + msg);
channel.basicAck(delivery.getEnvelope().getDeliveryTag(), false);
};
channel.basicConsume(QUEUE_NAME, false, deliverCallback, consumerTag -> {});
}
}
镜像集群
架构特点
Java
┌──────────────────────────────────────────────────┐
│ Mirror Queue: orders │
├─────────────┬─────────────┬──────────────────────┤
│ Node A │ Node B │ Node C │
│ (Master) │ (Slave) │ (Slave) │
│ messages │ messages │ messages │
│ ┌───────┐ │ ┌───────┐ │ ┌───────┐ │
│ │ m1 m2 │ │ │ m1 m2 │ │ │ m1 m2 │ │
│ └───────┘ │ └───────┘ │ └───────┘ │
└─────────────┴─────────────┴──────────────────────┘
- 消息体在Master和所有Slave节点完整复制
- Master负责读写,Slave同步复制消息
- Master宕机时,Slave自动选举升级为新的Master
- 通过Policy配置镜像规则,可按队列名模式匹配
镜像Policy配置(通过管理命令)
text
# 设置所有以 mirrored- 开头的队列为镜像,同步到所有节点
rabbitmqctl set_policy ha-all "^mirrored-" '{"ha-mode":"all"}' --apply-to queues
# 镜像到指定数量的节点
rabbitmqctl set_policy ha-two "^orders-" '{"ha-mode":"exactly","ha-params":2}' --apply-to queues
# 镜像到特定节点
rabbitmqctl set_policy ha-nodes "^logs-" '{"ha-mode":"nodes","ha-params":["rabbit@nodeB","rabbit@nodeC"]}' --apply-to queues
代码中声明镜像队列
text
import com.rabbitmq.client.*;
import java.util.HashMap;
import java.util.Map;
public class MirroredQueueProducer {
private static final String QUEUE_NAME = "mirrored-orders";
public static void main(String[] args) throws Exception {
ConnectionFactory factory = new ConnectionFactory();
factory.setHost("localhost");
factory.setPort(5672);
Connection connection = factory.newConnection();
Channel channel = connection.createChannel();
// 声明队列时设置镜像策略参数
Map<String, Object> arguments = new HashMap<>();
arguments.put("x-ha-policy", "all"); // 3.x旧版本参数,3.8+推荐使用Policy
arguments.put("x-queue-master-locator", "min-masters"); // 主节点定位策略
channel.queueDeclare(QUEUE_NAME, true, false, false, arguments);
// 发布消息(同步到所有镜像节点)
String message = "order-12345";
channel.basicPublish("", QUEUE_NAME,
MessageProperties.PERSISTENT_TEXT_PLAIN,
message.getBytes("UTF-8"));
System.out.println("发送消息: " + message);
channel.close();
connection.close();
}
}
镜像集群通过数据冗余提供高可用,但每次写入需同步到所有Slave,写性能随镜像节点数下降。普通集群适合水平扩展读能力,镜像集群适合对数据可靠性要求高的场景。
注意事项
性能取舍:普通集群无消息复制开销,写性能接近单节点;镜像集群每次写入需复制到所有Slave,延迟增加。
可用性差异:普通集群Master宕机后队列不可用;镜像集群Slave可自动接管,提供持续可用。
脑裂处理:网络分区时镜像集群可能出现数据不一致,RabbitMQ提供
pause_minority和autoheal两种分区处理策略。升级建议:RabbitMQ 3.8+推荐使用Quorum Queue替代镜像队列,基于Raft共识算法提供更可靠的一致性保证。
策略优先级:Policy可动态修改,新策略仅影响新创建的队列,已有队列需重建或等待重启生效。
要点总结
- 普通集群仅同步元数据,消息体存储在Master节点,性能高但可用性低
- 镜像集群完整复制消息体到多个节点,Master宕机时Slave可接管
- 镜像策略通过
rabbitmqctl set_policy配置,支持all、exactly、nodes三种模式 - 镜像集群写入性能随节点数下降,适合对可靠性要求高于性能的场景
- RabbitMQ 3.8+推荐使用Quorum Queue替代镜像队列
📝 发现内容有误?点击此处直接编辑