RabbitMQ 跨集群:Shovel 与 Federation 怎么选
两个插件都能把消息从一个 Broker 送到另一个,区别在于视角:Shovel 是「我要把这个队列的消息搬到那边去」,Federation 是「让那边的消息流在我这里也有一份」。选错了会在迁移或容灾时发现配置方式完全不对。
一、先说结论
- Shovel 是点对点搬运:从指定的源队列消费,重新发布到指定的目标交换机或队列,方向单一、配置明确。
- Federation 是链路复制:联邦交换机会把上游交换机收到的消息在下游「重放」一份;联邦队列则在本地没有消费者可用时,从上游队列拉取消息。
- 两者都不要求集群同版本:官方文档明确说明,参与的 Broker 可以运行不同版本的 RabbitMQ 和 Erlang,这正是它们与集群(cluster)的关键差别——集群要求版本一致且网络可靠。
- 一次性迁移、定向复制用 Shovel;长期的跨机房拓扑、多活消费用 Federation。
- 两者都不是高可用方案:它们跨的是 Broker,不负责单个 Broker 内部的数据冗余,后者靠仲裁队列(quorum queue)。
二、消息流向的差别
2.1 Shovel
Shovel 本质上是 Broker 内部运行的一个 AMQP 客户端:
- 连接到源(通常是本地)和目标(通常是远程)Broker;
- 从源队列消费消息;
- 发布到目标的交换机或队列;
- 目标确认之后,再向源队列发送 ack。
因为有确认机制,默认提供至少一次投递语义:网络中断时消息不会丢,但可能重复,下游要幂等。
配置方式(动态 Shovel,通过管理插件或 HTTP API 下发):
bash
rabbitmqctl set_parameter shovel migrate-orders '{
"src-protocol": "amqp091",
"src-uri": "amqp://user:***@source-host",
"src-queue": "orders",
"dest-protocol": "amqp091",
"dest-uri": "amqp://user:***@target-host",
"dest-exchange": "orders-ex"
}'Shovel 还可以把消息送到其他兼容 AMQP 的 Broker,这在从 RabbitMQ 迁出或对接第三方系统时很有用。
2.2 Federation
Federation 通过策略(policy)建立上下游链接,有两种形态:
- 联邦交换机(federated exchange):下游交换机会收到上游交换机收到的消息副本。适合「一份消息,多个机房都要消费」的广播型场景。
- 联邦队列(federated queue):下游队列在本地消费者空闲时,从上游队列拉取消息。适合把消费能力分散到多个机房,同一条消息只被一个消费者处理。
bash
# 定义上游
rabbitmqctl set_parameter federation-upstream bj-dc '{"uri":"amqp://user:***@bj-host","expires":3600000}'
# 用策略把匹配的交换机联邦起来
rabbitmqctl set_policy --apply-to exchanges federate-orders "^orders\." '{"federation-upstream":"bj-dc"}'Federation 的设计目标是在不可靠的广域网上工作:链接断开后会自动重连,消息在上游侧缓冲。
三、对比
| 维度 | Shovel | Federation |
|---|---|---|
| 视角 | 明确的源与目的地 | 上游与下游的拓扑关系 |
| 配置位置 | 运行 Shovel 的那个 Broker | 下游 Broker(通过 policy 匹配) |
| 粒度 | 单个队列 / 交换机 | 按策略匹配一批交换机或队列 |
| 方向 | 单向 | 单向,可以配置成多个上游形成网状 |
| 目标类型 | RabbitMQ 或其他 AMQP Broker | 只能是 RabbitMQ |
| 典型用途 | 迁移、定向复制、对接异构系统 | 跨机房拓扑、多活消费、广域分发 |
| 版本要求 | 不要求一致 | 不要求一致 |
四、怎么选
用 Shovel 的场景:
- 集群迁移:老集群的消息搬到新集群,迁移完成后删掉 Shovel。
- 定向复制:把生产环境某个队列的消息复制一份到分析或测试环境。
- 对接非 RabbitMQ:目标是其他 AMQP Broker。
- 灾备:把关键队列的消息持续送到备用站点。
用 Federation 的场景:
- 跨机房的长期拓扑:多个机房各有一套 RabbitMQ,消息需要互通。
- 广播型分发:一份消息要在多个区域被各自消费(联邦交换机)。
- 消费能力分散:一个队列的消息由多个机房的消费者分担(联邦队列)。
- 管理域分离:两个集群由不同团队维护、用户和权限各自独立。
五、部署与排查
bash
# 启用插件(集群中的每个节点都要启用)
rabbitmq-plugins enable rabbitmq_shovel rabbitmq_shovel_management
rabbitmq-plugins enable rabbitmq_federation rabbitmq_federation_management
# 查看状态
rabbitmqctl shovel_status
rabbitmqctl federation_status几个容易踩的点:
- 集群里每个节点都要启用插件,否则链接会在部分节点上不可用。
- TLS 证书校验:Erlang 26 起默认开启客户端对端证书校验,升级后原本能连上的 TLS 链接可能突然失败,需要正确配置 CA 与证书。
- 凭据管理:URI 里包含用户名密码,要用配置中心或密钥管理,不要写进代码仓库。
- 监控链接状态:把
shovel_status/federation_status接进监控,链接断开要告警;同时观察上游队列积压。 - 消息重复:两者都是至少一次语义,下游消费必须幂等。
六、常见误区
- 「Shovel 和 Federation 能替代集群」:它们在独立 Broker 之间搬运消息,不提供单个 Broker 的数据冗余与高可用。
- 「配置了就一定不丢消息」:源侧未确认、目标不可用时消息会堆积,需要监控和容量规划。
- 「Federation 是双向同步」:单个链接是单向的,双向需要在两侧各配一个,并注意避免消息回环。
- 「跨集群要求版本一致」:官方明确支持不同版本的 RabbitMQ 与 Erlang。
小结
选择很简单:要搬的是「某个队列的消息」,用 Shovel;要建的是「集群之间的长期拓扑」,用 Federation。 前者配置直接、适合一次性任务,后者用策略批量管理、适合常态化的跨机房结构。两者都提供至少一次语义,所以消费端的幂等是前提,而单个集群的高可用仍然要靠仲裁队列来解决。
消息重复与幂等消费的通用做法,见 Kafka 不丢、不重与 Exactly Once。
参考资料