# RabbitMQ 客户端最佳实践

基于腾讯云 TDMQ RabbitMQ 托管版官方文档，详细说明客户端编码的最佳实践规则和背景知识。

> 来源: https://www.tencentcloud.com/zh/document/product/1112/64344

## Table of Contents

- [1. 连接和通道管理](#1-连接和通道管理)
- [2. 生产和消费优化](#2-生产和消费优化)
- [3. 队列优化](#3-队列优化)
- [4. 客户端机制](#4-客户端机制)

---

## 1. 连接和通道管理

### 1.1 连接复用

**规则**: 每个进程一个连接，每个线程一个通道。

**原因**:
- 创建连接是"昂贵"操作：每个连接占用至少 100KB 内存，握手需要至少 7 个 TCP 数据包
- 通道（channel）是连接内的轻量级通信方式，创建和销毁成本低
- 理想模型：进程启动时创建一个连接，业务线程各自创建独立通道

**反模式**:
```
// BAD: 每次发送消息都创建连接
for msg in messages:
    conn = create_connection()  // 每次都握手，浪费资源
    ch = conn.channel()
    ch.publish(msg)
    conn.close()
```

### 1.2 通道线程安全

**规则**: 禁止在线程之间共享通道。

**原因**: 大多数 RabbitMQ 客户端的 channel 实现不是线程安全的。共享会导致消息处理出错、性能下降、连接异常。

### 1.3 生产消费隔离

**规则**: 生产者和消费者 MUST 使用独立的连接。

**原因**: RabbitMQ 有独特的流控机制。若生产者和消费者共用一个连接，当消费流量大触发流控时，会导致同连接上的生产者被流控——发送变慢甚至超时。

### 1.4 连接数量与管理接口

大量连接和通道会影响 RabbitMQ 管理接口性能。管理界面需要实时收集指标、分析数据和可视化展示，连接/通道数量增加会显著降低管理界面响应速度。合理控制连接数。

---

## 2. 生产和消费优化

### 2.1 禁止自动确认

**规则**: 消费者 MUST 使用手动 ACK，禁止 autoAck。

**原因**: RabbitMQ 提供"至少投递一次"的消费语义。自动确认会导致消息在处理异常时不重试，造成业务漏处理。

### 2.2 幂等处理

**规则**: 消费者 MUST 做幂等处理。

**原因**: 极端场景下（网络分区恢复、消费者重连等）可能重复投递消息。

**实现方式**: 在消息体中加入唯一业务标识（如订单号、事务 ID），消费端根据标识检查是否已处理。

### 2.3 CONSUME 优于 GET

**规则**: 使用 `basic.consume`（push 模式），禁止使用 `basic.get`（pull 模式）。

**原因**:
- `basic.get` 每消费一条消息需发送一个请求（拉模式），效率低
- `basic.consume` 一次可接收一批消息，由服务端推送，效率高
- 持续空拉会导致服务端 CPU 负载异常升高

### 2.4 消息负载优化

- 每秒处理的消息数量对性能影响远大于单条消息大小
- 频繁发送大量小消息比发送少量大消息对服务端压力更大
- 推荐将多条小消息合并为一条较大消息

**合并消息的注意事项**:
- 子消息处理失败时是否需要整体重传
- 合并处理可能增加单次消息处理时长，影响实时性

### 2.5 Prefetch Count 配置

Prefetch count 指定服务端同时向消费者推送的未确认消息数量。

**配置原则**:
- 过低：消费者频繁等待新消息，吞吐量低
- 过高：消息集中在部分消费者，负载不均

**场景配置表**:

| 场景 | 推荐 prefetch | 说明 |
|------|:------------:|------|
| 消费速度快、处理时间短 | 250 | 提高吞吐量 |
| 处理时间稳定、网络可控 | RTT / 平均处理时间 | 精确匹配处理能力 |
| 消费者数量多、处理时间短 | 较低值 | 均匀分配 |
| 消费者数量多或处理时间长 | 1 | 确保严格均匀分配 |

**注意事项**:
- autoAck 模式下 prefetch 不生效
- 禁止 prefetch=0（无限制），会导致单个消费者接收全部消息并耗尽内存
- global prefetch 已废弃，不要使用

---

## 3. 队列优化

### 3.1 控制队列消息积压

积压消息占用大量内存，RabbitMQ 会将消息刷到磁盘，导致处理变慢。重启时需要重建消息索引，节点间同步消息也需要额外时间。

**解决方案**: 提高消费能力、设置 `max-length` 限制队列长度、设置消息 TTL。

### 3.2 流量拆分

RabbitMQ 单个队列的性能受限于单核 CPU，无法横向扩展。将流量拆分到不同队列以利用多核能力。

### 3.3 队列数量控制

每个队列占用系统资源，管理插件需要为每个队列收集和计算指标。队列过多影响集群性能。根据业务需求合理规划队列数量。

### 3.4 优先级队列

每个优先级单独处理，占用额外资源。大多数场景 3-5 个优先级即可，不要设置过多（10+）。

### 3.5 队列自动删除

三种方式：

1. **队列 TTL** — 配置生存时间，超时未使用自动删除
2. **Auto-Delete 队列** — 最后一个消费者取消订阅时删除
3. **Exclusive 队列** — 声明连接关闭时立即自动删除，仅允许声明连接操作

### 3.6 仲裁队列（Quorum Queues）

RabbitMQ 3.8+ 引入，基于 Raft 协议的复制队列。**RabbitMQ 3.13 推荐优先选择仲裁队列替代传统镜像队列**，提供更高的数据安全性和可用性。

### 3.7 惰性队列（Lazy Queues）

适用于 RabbitMQ < 3.12。消息直接持久化到磁盘而非保留于内存，降低内存使用但可能增加延迟。适用于批处理或消费者持续落后于生产者的场景。

**注意**: RabbitMQ 3.12+ 所有经典队列默认采用相同行为，无需单独配置。

---

## 4. 客户端机制

### 4.1 重连机制

Broker 在以下场景会重启：OOM、容器母机故障、集群升配等运维操作。客户端 MUST 确保自动恢复连接。

**各语言重连方式**:

| 语言 | 方式 | 关键 API |
|------|------|---------|
| Java | 内置自动重连 | `factory.setAutomaticRecoveryEnabled(true)` |
| .NET | 内置自动重连 | `factory.AutomaticRecoveryEnabled = true` |
| Go | 应用层实现 | 监听 `conn.NotifyClose()`，重建连接和通道 |
| Python (pika) | 应用层实现 | 捕获 `ConnectionClosedByBroker`，重建连接 |
| PHP | 应用层实现 | 捕获 `AMQPConnectionClosedException`，重建连接 |

### 4.2 心跳（Heartbeat）

**规则**: 禁止设置 heartbeat=0（关闭心跳检测）。

**原因**: 服务端默认心跳 60 秒，最终值由客户端和服务端协商。关闭心跳后服务端无法自动剔除长期无数据交互的无用连接，可能引起连接泄露。

### 4.3 网络分区应对

腾讯云 RabbitMQ 采用 autoheal 模式：自动决出获胜分区，重启非信任分区内的 Broker。

**客户端应对**:
- **生产者**: 设置 mandatory，处理 `basic.return`，及时感知消息路由失败
- **消费者**: 做好幂等处理，应对分区恢复期可能的消息重复投递
