RabbitMQ 完全指南

一、RabbitMQ 是什么?

1.1 定义

RabbitMQ 是一个开源的消息中间件(Message Broker),基于 AMQP(Advanced Message Queuing Protocol)协议实现,使用 Erlang 语言开发。

复制代码
核心定位:
┌─────────────────────────────────────────────┐
│         消息中间件(Message Broker)          │
│                                             │
│  ├─ 异步通信                                 │
│  ├─ 应用解耦                                 │
│  ├─ 流量削峰                                 │
│  ├─ 消息路由                                 │
│  └─ 可靠传递                                 │
└─────────────────────────────────────────────┘

1.2 消息队列的演进

复制代码
同步调用(传统方式):
应用 A ──────直接调用──────> 应用 B
   │                        │
   └────── 等待响应 ─────────┘
问题:
├─ 强耦合
├─ 同步阻塞
└─ 性能瓶颈

异步调用(消息队列):
应用 A ───发送消息──> [消息队列] ───消费消息──> 应用 B
   │                                         │
   └── 立即返回                               └── 异步处理

优势:
├─ 解耦
├─ 异步
├─ 削峰
└─ 可靠

1.3 RabbitMQ vs 其他消息队列

特性 RabbitMQ Kafka RocketMQ ActiveMQ
协议 AMQP 自定义 自定义 JMS、AMQP
语言 Erlang Scala/Java Java Java
吞吐量 万级 百万级 十万级 万级
时效性 微秒级 毫秒级 毫秒级 毫秒级
可用性 高(主从) 非常高(分布式) 高(主从)
消息丢失 理论不丢
功能特性 丰富(路由) 简单(日志) 丰富 较丰富
适用场景 复杂路由、实时性 大数据、日志 金融、电商 企业集成
社区支持 活跃 非常活跃 活跃 较少

1.4 RabbitMQ 特点

复制代码
核心特点:

1. 可靠性
   ├─ 消息持久化
   ├─ 消息确认(ACK)
   ├─ 发布确认(Confirm)
   └─ 高可用(镜像队列)

2. 灵活的路由
   ├─ Direct(直连)
   ├─ Topic(主题)
   ├─ Fanout(广播)
   └─ Headers(头部)

3. 集群与高可用
   ├─ 集群模式
   ├─ 镜像队列
   └─ 联邦模式

4. 多语言支持
   ├─ Java、Python、Go
   ├─ .NET、PHP、Ruby
   └─ 几乎所有主流语言

5. 管理界面
   ├─ Web 管理控制台
   ├─ HTTP API
   └─ 命令行工具

6. 插件生态
   ├─ 延迟队列
   ├─ 消息追踪
   └─ 联邦/Shovel

二、RabbitMQ 核心作用

2.1 应用解耦

问题场景:订单系统需要调用库存、支付、物流等多个系统

传统方式(强耦合)

java 复制代码
public class OrderService {
    @Autowired
    private InventoryService inventoryService;  // 库存服务
    @Autowired
    private PaymentService paymentService;      // 支付服务
    @Autowired
    private LogisticsService logisticsService;  // 物流服务

    public void createOrder(Order order) {
        // 1. 创建订单
        saveOrder(order);

        // 2. 调用库存服务(强依赖)
        inventoryService.decreaseStock(order);

        // 3. 调用支付服务(强依赖)
        paymentService.pay(order);

        // 4. 调用物流服务(强依赖)
        logisticsService.ship(order);

        // 问题:
        // - 任何一个服务挂了,整个流程失败
        // - 新增服务需要修改代码
        // - 各服务强耦合
    }
}

消息队列方式(解耦)

java 复制代码
public class OrderService {
    @Autowired
    private RabbitTemplate rabbitTemplate;

    public void createOrder(Order order) {
        // 1. 创建订单
        saveOrder(order);

        // 2. 发送消息到消息队列
        rabbitTemplate.convertAndSend("order.exchange", "order.created", order);

        // 完成!订单服务不关心后续处理
    }
}

// 库存服务(独立消费)
@RabbitListener(queues = "inventory.queue")
public void handleOrder(Order order) {
    decreaseStock(order);
}

// 支付服务(独立消费)
@RabbitListener(queues = "payment.queue")
public void handleOrder(Order order) {
    pay(order);
}

// 优势:
// - 订单服务不依赖其他服务
// - 新增服务只需订阅消息
// - 各服务独立开发、部署

2.2 异步处理

问题场景:用户注册需要发送邮件、短信

同步处理(慢)

java 复制代码
public void register(User user) {
    // 1. 保存用户(50ms)
    saveUser(user);

    // 2. 发送邮件(200ms)
    sendEmail(user);

    // 3. 发送短信(100ms)
    sendSMS(user);

    // 总耗时:350ms
    // 用户等待 350ms 才能看到注册成功
}

异步处理(快)

java 复制代码
public void register(User user) {
    // 1. 保存用户(50ms)
    saveUser(user);

    // 2. 发送消息(1ms)
    rabbitTemplate.convertAndSend("user.exchange", "user.registered", user);

    // 总耗时:51ms
    // 用户立即看到注册成功
    // 邮件、短信异步发送
}

// 邮件服务异步消费
@RabbitListener(queues = "email.queue")
public void sendEmail(User user) {
    // 异步发送邮件
}

// 短信服务异步消费
@RabbitListener(queues = "sms.queue")
public void sendSMS(User user) {
    // 异步发送短信
}

性能对比

复制代码
同步处理:350ms(用户等待)
异步处理:51ms(用户无感知)
性能提升:7倍

2.3 流量削峰

问题场景:秒杀活动,瞬间 10 万请求

无消息队列(系统崩溃)

复制代码
秒杀开始
  │
10万请求 ──────> 应用服务器(最多处理1000 QPS)
                    │
                    ├─ 9.9万请求被拒绝
                    ├─ 数据库崩溃
                    └─ 系统宕机

有消息队列(削峰)

复制代码
秒杀开始
  │
10万请求 ──────> 应用服务器
                    │
                    ├─ 快速写入消息队列
                    └─ 立即返回"排队中"
                         │
                         ↓
                   [消息队列](缓冲)
                    100,000 条消息
                         │
                         ↓
                   消费者(按自己的速度处理)
                    1000 QPS 稳定消费
                         │
                         ↓
                   100秒处理完成
                   系统稳定运行

代码示例

java 复制代码
// 秒杀控制器
@PostMapping("/seckill")
public String seckill(Long productId, Long userId) {
    // 1. 快速写入消息队列
    SeckillMessage msg = new SeckillMessage(productId, userId);
    rabbitTemplate.convertAndSend("seckill.exchange", "", msg);

    // 2. 立即返回
    return "排队中,请稍候查询结果";
}

// 秒杀消费者(控制处理速度)
@RabbitListener(queues = "seckill.queue")
public void handleSeckill(SeckillMessage msg) {
    // 按照系统能力处理(1000 QPS)
    seckillService.doSeckill(msg.getProductId(), msg.getUserId());
}

2.4 数据分发

问题场景:订单创建后,需要通知多个系统

复制代码
订单系统创建订单
    │
    ├──> 库存系统(扣减库存)
    ├──> 支付系统(发起支付)
    ├──> 物流系统(准备发货)
    ├──> 积分系统(增加积分)
    └──> 数据分析系统(统计)

使用 Fanout Exchange(广播模式)
java 复制代码
// 订单服务发送消息
rabbitTemplate.convertAndSend("order.fanout", "", order);

// 多个系统独立消费
@RabbitListener(queues = "inventory.queue")  // 库存系统
@RabbitListener(queues = "payment.queue")    // 支付系统
@RabbitListener(queues = "logistics.queue")  // 物流系统
@RabbitListener(queues = "points.queue")     // 积分系统
@RabbitListener(queues = "analytics.queue")  // 数据分析

三、核心概念与架构

3.1 核心组件

复制代码
RabbitMQ 核心组件:

┌─────────────┐
│  Producer   │  生产者(发送消息)
│  (生产者)    │
└──────┬──────┘
       │ 发送消息
       ↓
┌─────────────────────────────────────────────────┐
│              RabbitMQ Broker                    │
│                                                 │
│  ┌──────────────┐      ┌──────────────┐        │
│  │   Exchange   │──────│    Queue     │        │
│  │   (交换机)    │ 路由  │   (队列)     │        │
│  └──────────────┘      └──────┬───────┘        │
│         │                     │                │
│      绑定关系                消息存储            │
│    (Binding)                                   │
│         │                     │                │
│  ┌──────────────┐      ┌──────────────┐        │
│  │   Binding    │      │   Message    │        │
│  │   (绑定)     │      │   (消息)      │        │
│  └──────────────┘      └──────────────┘        │
│                                                 │
└────────────────────────────┬────────────────────┘
                             │ 消费消息
                             ↓
                      ┌─────────────┐
                      │  Consumer   │  消费者(接收消息)
                      │  (消费者)    │
                      └─────────────┘

3.2 核心概念详解

3.2.1 Producer(生产者)
复制代码
生产者:
├─ 创建消息
├─ 发送到 Exchange
└─ 不关心消息如何路由

示例:
Producer → Message(routingKey="order.created") → Exchange
3.2.2 Exchange(交换机)
复制代码
交换机的作用:
├─ 接收生产者发送的消息
├─ 根据路由规则分发到队列
└─ 4种类型:Direct、Topic、Fanout、Headers

交换机属性:
├─ Name:交换机名称
├─ Type:类型(direct/topic/fanout/headers)
├─ Durability:是否持久化
├─ Auto-delete:无队列绑定时自动删除
└─ Arguments:扩展参数

4种交换机类型

类型 路由规则 使用场景
Direct 精确匹配 routing key 简单路由、点对点
Topic 模式匹配(通配符) 复杂路由、订阅
Fanout 广播(忽略 routing key) 消息广播
Headers 匹配消息头属性 复杂匹配
3.2.3 Queue(队列)
复制代码
队列:
├─ 存储消息(缓冲区)
├─ FIFO(先进先出)
├─ 消费者从队列取消息
└─ 可以设置持久化、优先级、TTL等

队列属性:
├─ Name:队列名称
├─ Durable:是否持久化
├─ Exclusive:是否独占
├─ Auto-delete:无消费者时自动删除
├─ Arguments:
│   ├─ x-message-ttl:消息过期时间
│   ├─ x-max-length:队列最大长度
│   ├─ x-max-priority:最大优先级
│   └─ x-dead-letter-exchange:死信交换机
└─ ...
3.2.4 Binding(绑定)
复制代码
绑定:
├─ 连接 Exchange 和 Queue
├─ 包含 routing key(路由键)
└─ 定义消息如何路由

示例:
Exchange(order.exchange) ──[routing key: order.created]──> Queue(order.queue)
3.2.5 Message(消息)
复制代码
消息组成:
├─ 消息体(Body)
│   └─ 实际的业务数据(字节数组)
│
├─ 属性(Properties)
│   ├─ content-type:内容类型(application/json)
│   ├─ content-encoding:编码(UTF-8)
│   ├─ delivery-mode:投递模式(1=非持久化,2=持久化)
│   ├─ priority:优先级(0-9)
│   ├─ correlation-id:关联ID
│   ├─ reply-to:回复队列
│   ├─ expiration:过期时间
│   ├─ message-id:消息ID
│   ├─ timestamp:时间戳
│   ├─ type:消息类型
│   ├─ user-id:用户ID
│   └─ app-id:应用ID
│
└─ 自定义头(Headers)
    └─ key-value 键值对
3.2.6 Consumer(消费者)
复制代码
消费者:
├─ 订阅队列
├─ 接收消息
├─ 处理业务逻辑
└─ 发送 ACK(确认)

消费模式:
├─ Push(推模式):服务器主动推送(推荐)
└─ Pull(拉模式):客户端主动拉取

确认模式:
├─ 自动确认(autoAck=true):收到消息立即确认
├─ 手动确认(autoAck=false):处理完成后确认
└─ 不确认:消息重新入队

3.3 消息流转过程

复制代码
完整的消息流转:

1. 生产者发送消息
   Producer
      │
      ├─ 创建消息(Message)
      ├─ 指定交换机(Exchange)
      ├─ 指定路由键(Routing Key)
      └─ 发送(publish)
           ↓
2. 到达 RabbitMQ Broker
   Connection(连接)
      ├─ TCP 连接
      └─ Channel(信道)
           ↓
3. 路由到交换机
   Exchange
      ├─ 接收消息
      ├─ 根据类型和路由键匹配
      └─ 分发到绑定的队列
           ↓
4. 存储到队列
   Queue
      ├─ 存储消息(内存/磁盘)
      ├─ 等待消费者
      └─ 维护消费位置
           ↓
5. 消费者消费
   Consumer
      ├─ 订阅队列(subscribe)
      ├─ 接收消息(deliver)
      ├─ 处理业务逻辑
      └─ 发送确认(ACK)
           ↓
6. 删除消息
   RabbitMQ
      └─ 收到 ACK 后删除消息

3.4 连接与信道

复制代码
连接与信道的关系:

Application
    │
    ├─ Connection(TCP 连接)
    │     ├─ 耗资源、重量级
    │     └─ 一个应用通常只有1个连接
    │
    └─ Channel(信道,轻量级)
          ├─ 在 Connection 内的虚拟连接
          ├─ 多路复用
          ├─ 独立的 AMQP 会话
          └─ 一个 Connection 可以有多个 Channel

┌─────────────────────────────────────────┐
│         Application                     │
│                                         │
│  ┌────────────────────────────────────┐ │
│  │    Connection (TCP)                │ │
│  │                                    │ │
│  │  ┌──────────┐  ┌──────────┐       │ │
│  │  │Channel 1 │  │Channel 2 │ ...   │ │
│  │  └──────────┘  └──────────┘       │ │
│  └────────────────────────────────────┘ │
└─────────────────────────────────────────┘
                   │
                   ↓
           RabbitMQ Broker

为什么需要 Channel?
├─ 减少 TCP 连接数(Connection 重量级)
├─ 提高并发(一个连接支持多个信道)
└─ 线程安全(每个线程一个 Channel)

四、底层原理详解

4.1 AMQP 协议

4.1.1 AMQP 概述
复制代码
AMQP(Advanced Message Queuing Protocol)
高级消息队列协议

协议特点:
├─ 应用层协议(类似 HTTP)
├─ 二进制协议(高效)
├─ 跨平台、跨语言
└─ 支持多种消息模式

协议版本:
├─ AMQP 0-9-1(RabbitMQ 主要支持)
└─ AMQP 1.0(RabbitMQ 通过插件支持)
4.1.2 AMQP 模型
复制代码
AMQP 模型:

Publisher ──publish──> Exchange ──routing──> Queue ──consume──> Consumer
                         │                    │
                         │                    │
                      绑定规则              消息存储
                     (Binding)             (Message)
4.1.3 AMQP 帧结构
复制代码
AMQP 帧(Frame):

┌──────────────────────────────────────┐
│  Frame Header (7 bytes)              │
│  ├─ Type (1 byte):帧类型             │
│  ├─ Channel (2 bytes):信道ID         │
│  └─ Size (4 bytes):帧大小            │
├──────────────────────────────────────┤
│  Payload(可变长度)                  │
│  └─ 实际数据内容                      │
├──────────────────────────────────────┤
│  Frame End (1 byte):帧结束标记(0xCE) │
└──────────────────────────────────────┘

帧类型:
├─ 1:Method Frame(方法帧)
├─ 2:Header Frame(内容头帧)
├─ 3:Body Frame(消息体帧)
└─ 8:Heartbeat Frame(心跳帧)

4.2 消息投递流程

4.2.1 发送消息流程
复制代码
1. 创建连接
   Client ──TCP Handshake──> RabbitMQ
      ├─ AMQP 协议握手
      ├─ 认证(用户名/密码)
      └─ 虚拟主机选择

2. 创建信道
   Connection ──Open Channel──> Channel
      └─ 获取 Channel ID

3. 声明交换机(可选)
   Channel ──Exchange.Declare──> Exchange
      ├─ 交换机名称
      ├─ 类型(direct/topic/fanout/headers)
      ├─ 持久化(durable)
      └─ 其他参数

4. 声明队列(可选)
   Channel ──Queue.Declare──> Queue
      ├─ 队列名称
      ├─ 持久化(durable)
      ├─ 独占(exclusive)
      └─ 自动删除(auto-delete)

5. 绑定队列(可选)
   Channel ──Queue.Bind──> Binding
      ├─ 队列名称
      ├─ 交换机名称
      └─ 路由键(routing key)

6. 发送消息
   Channel ──Basic.Publish──> Message
      ├─ 交换机名称
      ├─ 路由键
      ├─ 消息属性
      └─ 消息体
           ↓
7. 路由消息
   Exchange ──Route──> Queue
      ├─ 根据交换机类型
      ├─ 根据路由键
      └─ 匹配绑定关系
           ↓
8. 存储消息
   Queue ──Store──> Disk/Memory
      └─ 根据持久化设置
4.2.2 消费消息流程
复制代码
1. 订阅队列
   Consumer ──Basic.Consume──> Queue
      ├─ 队列名称
      ├─ 消费者标签(consumer tag)
      ├─ 自动确认(auto ack)
      └─ 其他参数

2. 接收消息(Push 模式)
   Queue ──Basic.Deliver──> Consumer
      ├─ 消息属性
      ├─ 消息体
      └─ delivery tag(投递标签)

3. 处理消息
   Consumer
      └─ 执行业务逻辑

4. 确认消息
   Consumer ──Basic.Ack──> RabbitMQ
      └─ delivery tag
           ↓
5. 删除消息
   RabbitMQ
      └─ 从队列删除消息

拒绝消息(可选):
   Consumer ──Basic.Nack/Reject──> RabbitMQ
      ├─ delivery tag
      └─ requeue(是否重新入队)

4.3 路由算法

4.3.1 Direct Exchange 路由
java 复制代码
// 精确匹配路由键
public void route(Message message, String routingKey) {
    for (Binding binding : bindings) {
        if (binding.getRoutingKey().equals(routingKey)) {
            queue.enqueue(message);
        }
    }
}

示例:
Exchange: order.direct
Bindings:
  ├─ routing key: order.created  → queue: order.queue
  ├─ routing key: order.paid     → queue: payment.queue
  └─ routing key: order.shipped  → queue: logistics.queue

消息: routing key = "order.created"
结果: 只路由到 order.queue
4.3.2 Topic Exchange 路由
java 复制代码
// 模式匹配(支持通配符)
// * 匹配一个单词
// # 匹配零个或多个单词
public void route(Message message, String routingKey) {
    for (Binding binding : bindings) {
        if (matchPattern(binding.getPattern(), routingKey)) {
            queue.enqueue(message);
        }
    }
}

示例:
Exchange: order.topic
Bindings:
  ├─ pattern: order.*        → queue: all-orders.queue
  ├─ pattern: order.created  → queue: order.queue
  ├─ pattern: order.#        → queue: archive.queue
  └─ pattern: *.paid         → queue: payment.queue

消息: routing key = "order.created"
匹配:
  ├─ order.*        ✅ 匹配(order.created)
  ├─ order.created  ✅ 匹配
  ├─ order.#        ✅ 匹配(order.created)
  └─ *.paid         ❌ 不匹配

结果: 路由到 all-orders.queue、order.queue、archive.queue
4.3.3 Fanout Exchange 路由
java 复制代码
// 广播模式(忽略路由键)
public void route(Message message, String routingKey) {
    for (Binding binding : bindings) {
        queue.enqueue(message);  // 发送到所有绑定的队列
    }
}

示例:
Exchange: order.fanout
Bindings:
  ├─ queue: inventory.queue
  ├─ queue: payment.queue
  └─ queue: logistics.queue

消息: routing key = "任意值"
结果: 路由到所有绑定的队列

4.4 消息确认机制

4.4.1 生产者确认(Publisher Confirm)
复制代码
发送流程(Confirm 模式):

Producer ──[1] Publish Message──> RabbitMQ
                                      │
                                 [2] 路由消息
                                      ├─ 成功路由到队列
                                      │    └─> [3] Basic.Ack
                                      │              │
Producer <──────[4] 收到确认──────────┘
                                      │
                                      └─ 路由失败
                                           └─> [3] Basic.Nack
                                                      │
Producer <──────[4] 收到拒绝──────────────────────────┘

代码示例:
channel.confirmSelect();  // 启用确认模式
channel.basicPublish(exchange, routingKey, props, body);
channel.waitForConfirms();  // 等待确认
4.4.2 消费者确认(Consumer ACK)
复制代码
消费流程(手动确认):

RabbitMQ ──[1] Deliver Message──> Consumer
                                      │
                                 [2] 处理消息
                                      │
                                  ┌───┴───┐
                                  │       │
                            [3a] 成功  [3b] 失败
                                  │       │
                         Basic.Ack  Basic.Nack
                                  │       │
RabbitMQ <────[4a] 删除消息────────┘       │
                                           │
RabbitMQ <────[4b] 重新入队────────────────┘

确认方式:
├─ Basic.Ack:确认消息(删除)
├─ Basic.Nack:拒绝消息(可重新入队)
└─ Basic.Reject:拒绝单条消息

代码示例:
channel.basicConsume(queue, false, (tag, message) -> {
    try {
        processMessage(message);
        channel.basicAck(message.getEnvelope().getDeliveryTag(), false);
    } catch (Exception e) {
        channel.basicNack(message.getEnvelope().getDeliveryTag(), false, true);
    }
});

4.5 消息持久化

复制代码
持久化三要素(全部开启才能保证消息不丢失):

1. 交换机持久化
   channel.exchangeDeclare(
       "order.exchange",
       "direct",
       true  // durable = true(持久化)
   );

2. 队列持久化
   channel.queueDeclare(
       "order.queue",
       true,  // durable = true(持久化)
       false,
       false,
       null
   );

3. 消息持久化
   AMQP.BasicProperties props = new AMQP.BasicProperties.Builder()
       .deliveryMode(2)  // 2 = 持久化,1 = 非持久化
       .build();
   channel.basicPublish(exchange, routingKey, props, body);

持久化流程:
Message ──> RabbitMQ
              │
         [1] 接收消息
              │
         [2] 写入磁盘
              ├─ 消息内容
              ├─ 索引信息
              └─ 元数据
              │
         [3] 返回确认
              │
         [4] 内存缓存
              │
         [5] 异步刷盘

五、数据存储机制

5.1 存储架构

复制代码
RabbitMQ 存储架构:

┌────────────────────────────────────────────┐
│            内存存储(RAM)                  │
│  ├─ 队列元数据                              │
│  ├─ 交换机元数据                            │
│  ├─ 绑定关系                                │
│  └─ 消息索引                                │
└────────────────────────────────────────────┘
                   │
                   ↓
┌────────────────────────────────────────────┐
│          磁盘存储(Mnesia + 文件)           │
│                                            │
│  ┌──────────────────────────────────────┐ │
│  │  Mnesia 数据库(元数据)               │ │
│  │  ├─ 队列定义                          │ │
│  │  ├─ 交换机定义                        │ │
│  │  ├─ 绑定关系                          │ │
│  │  ├─ 虚拟主机                          │ │
│  │  └─ 用户权限                          │ │
│  └──────────────────────────────────────┘ │
│                                            │
│  ┌──────────────────────────────────────┐ │
│  │  消息存储(Message Store)             │ │
│  │  ├─ 消息内容                          │ │
│  │  ├─ 消息索引                          │ │
│  │  └─ 持久化队列消息                     │ │
│  └──────────────────────────────────────┘ │
└────────────────────────────────────────────┘

5.2 Mnesia 数据库

复制代码
Mnesia(Erlang 内置分布式数据库):

作用:
├─ 存储 RabbitMQ 元数据
├─ 支持集群
└─ 支持事务

存储内容:
├─ Virtual Host(虚拟主机)
├─ Exchange(交换机)
├─ Queue(队列)
├─ Binding(绑定)
├─ User(用户)
├─ Permission(权限)
└─ Policy(策略)

存储位置:
└─ /var/lib/rabbitmq/mnesia/

文件结构:
/var/lib/rabbitmq/mnesia/rabbit@hostname/
  ├─ DECISION_TAB.LOG
  ├─ LATEST.LOG
  ├─ nodes_running_at_shutdown
  ├─ rabbit_durable_exchange.DCD
  ├─ rabbit_durable_queue.DCD
  ├─ rabbit_durable_route.DCD
  └─ schema.DAT

5.3 消息存储

5.3.1 消息存储文件
复制代码
消息存储目录:
/var/lib/rabbitmq/mnesia/rabbit@hostname/msg_stores/vhosts/

文件类型:
├─ .rdq(Queue Index):队列索引文件
│   ├─ 消息位置
│   ├─ 消息状态(未投递/已投递/已确认)
│   └─ 消息序号
│
├─ .idx(Message Index):消息索引文件
│   ├─ 消息ID
│   ├─ 文件位置
│   └─ 消息大小
│
└─ .ets(Segment File):消息存储文件
    └─ 实际的消息内容

文件组织:
msg_store_persistent/(持久化消息)
  ├─ 0.rdq
  ├─ 1.rdq
  ├─ msg_0.ets
  ├─ msg_1.ets
  └─ ...

msg_store_transient/(临时消息)
  └─ 同上
5.3.2 消息存储流程
java 复制代码
// 简化的消息存储流程
public void storeMessage(Message message) {
    // 1. 判断消息大小
    if (message.size() < EMBED_THRESHOLD) {
        // 小消息(< 4KB):直接存储在队列索引
        queueIndex.embedMessage(message);
    } else {
        // 大消息:存储在消息文件
        // 1.1 写入消息文件
        long offset = messageStore.write(message);

        // 1.2 写入索引
        messageIndex.write(message.getId(), offset, message.size());

        // 1.3 队列索引记录引用
        queueIndex.addReference(message.getId());
    }

    // 2. 如果是持久化消息
    if (message.isPersistent()) {
        // 异步刷盘
        messageStore.flush();
    }
}
5.3.3 消息存储优化
复制代码
优化策略:

1. 批量写入
   ├─ 多条消息合并写入
   └─ 减少磁盘 I/O

2. 异步刷盘
   ├─ 先写内存缓冲区
   ├─ 定期(默认每秒)刷盘
   └─ 提高吞吐量

3. 内存索引
   ├─ 消息索引缓存在内存
   ├─ 快速查找消息位置
   └─ 定期持久化

4. 垃圾回收
   ├─ 定期合并文件
   ├─ 删除已确认消息
   └─ 回收磁盘空间

5. 流量控制
   ├─ 内存告警(默认 40%)
   ├─ 磁盘告警(默认 50GB)
   └─ 阻塞生产者

5.4 内存管理

复制代码
内存管理策略:

1. 内存告警(Memory Alarm)
   ├─ 默认阈值:系统内存的 40%
   ├─ 触发条件:RabbitMQ 使用内存超过阈值
   └─ 行为:阻塞所有生产者连接

2. 内存分配
   ├─ 连接和信道
   ├─ 队列进程
   ├─ 消息(内存队列)
   ├─ Mnesia 数据库
   ├─ 插件
   └─ 管理界面

3. 内存换页(Paging)
   ├─ 队列达到一定长度
   ├─ 消息从内存换到磁盘
   ├─ 保留消息索引在内存
   └─ 消费时再读回内存

内存使用示例:
┌────────────────────────────────────┐
│  RabbitMQ 内存使用(假设 4GB 总内存) │
├────────────────────────────────────┤
│  1.6GB(40%) ← 内存告警阈值        │
│                                    │
│  当前使用:1.2GB                    │
│  ├─ 连接/信道:200MB                │
│  ├─ 队列进程:300MB                 │
│  ├─ 消息:600MB                     │
│  └─ 其他:100MB                     │
└────────────────────────────────────┘

5.5 磁盘管理

复制代码
磁盘告警(Disk Alarm):

1. 触发条件
   ├─ 可用磁盘空间 < 阈值(默认 50GB)
   └─ 可用磁盘空间 < 磁盘总量的 10%

2. 告警行为
   ├─ 阻塞所有生产者
   ├─ 停止接收消息
   └─ 允许消费者继续消费

3. 恢复条件
   └─ 磁盘空间恢复到阈值以上

磁盘空间分配:
/var/lib/rabbitmq/
  ├─ mnesia/(元数据,通常很小)
  │    └─ 数百 MB
  │
  └─ msg_stores/(消息存储,主要占用)
       └─ 取决于消息量和持久化设置

磁盘清理策略:
├─ 设置消息 TTL(过期自动删除)
├─ 设置队列最大长度
├─ 定期消费消息
└─ 监控磁盘使用

六、数据结构详解

6.1 队列数据结构

erlang 复制代码
% RabbitMQ 队列内部数据结构(Erlang)
-record(amqqueue, {
    name,              % 队列名称 {resource, VHost, queue, Name}
    durable,           % 是否持久化 (true/false)
    auto_delete,       % 是否自动删除 (true/false)
    exclusive_owner,   % 独占所有者 (none | connection pid)
    arguments,         % 参数列表 (list)
    pid,               % 队列进程 PID
    slave_pids,        % 镜像队列从节点 PID 列表
    sync_slave_pids,   % 同步的从节点 PID
    policy,            % 应用的策略
    gm_pids,           % 组成员 PID
    state              % 队列状态
}).

队列内存结构

复制代码
Queue Process(队列进程)
  │
  ├─ Message Queue(消息队列,内存)
  │   ├─ Unacknowledged Messages(未确认消息)
  │   │    └─ {msg_id, delivery_tag, consumer_tag, message}
  │   │
  │   └─ Ready Messages(待投递消息)
  │        └─ FIFO 队列
  │
  ├─ Queue Index(队列索引)
  │   ├─ Message Position(消息位置)
  │   ├─ Message Status(消息状态)
  │   │    ├─ alpha:内存 + 磁盘(消息和索引都在内存)
  │   │    ├─ beta:磁盘(消息在磁盘,索引在内存)
  │   │    ├─ gamma:磁盘(消息和索引都在磁盘)
  │   │    └─ delta:磁盘(批量消息)
  │   └─ Sequence Number(序列号)
  │
  └─ Backing Queue(后备队列,实现)
       └─ rabbit_variable_queue(默认实现)

6.2 消息数据结构

erlang 复制代码
% 消息结构
-record(basic_message, {
    exchange_name,     % 交换机名称 {resource, VHost, exchange, Name}
    routing_keys,      % 路由键列表 [binary()]
    content,           % 消息内容
    id,                % 消息 ID (binary())
    is_persistent      % 是否持久化 (boolean())
}).

% 消息内容
-record(content, {
    class_id,          % 类型 ID (60 = basic)
    properties,        % 消息属性
    properties_bin,    % 二进制属性
    payload_fragments_rev  % 消息体片段(反序)
}).

% 消息属性
-record('P_basic', {
    content_type,      % 内容类型 (<<"application/json">>)
    content_encoding,  % 编码 (<<"UTF-8">>)
    headers,           % 自定义头 [{binary(), any()}]
    delivery_mode,     % 投递模式 (1 | 2)
    priority,          % 优先级 (0-9)
    correlation_id,    % 关联 ID (binary())
    reply_to,          % 回复队列 (binary())
    expiration,        % 过期时间 (binary())
    message_id,        % 消息 ID (binary())
    timestamp,         % 时间戳 (integer())
    type,              % 类型 (binary())
    user_id,           % 用户 ID (binary())
    app_id             % 应用 ID (binary())
}).

6.3 交换机数据结构

erlang 复制代码
% 交换机结构
-record(exchange, {
    name,              % 交换机名称 {resource, VHost, exchange, Name}
    type,              % 类型 (direct | topic | fanout | headers)
    durable,           % 是否持久化 (true/false)
    auto_delete,       % 是否自动删除 (true/false)
    internal,          % 是否内部使用 (true/false)
    arguments,         % 参数列表 (list)
    policy             % 应用的策略
}).

% 绑定结构
-record(binding, {
    source,            % 源交换机 {resource, VHost, exchange, Name}
    key,               % 路由键 (binary())
    destination,       % 目标队列/交换机 {resource, VHost, queue/exchange, Name}
    args               % 绑定参数 (list)
}).

6.4 索引数据结构

复制代码
队列索引文件(.idx):

┌─────────────────────────────────────────┐
│         Queue Index File                │
├─────────────────────────────────────────┤
│  Segment 1 (16 MB)                      │
│  ├─ Entry 1                             │
│  │   ├─ Sequence Number: 1              │
│  │   ├─ Message ID: abc123              │
│  │   ├─ Status: published               │
│  │   ├─ Offset: 0                       │
│  │   └─ Size: 1024 bytes                │
│  │                                       │
│  ├─ Entry 2                             │
│  │   ├─ Sequence Number: 2              │
│  │   └─ ...                             │
│  └─ ...                                 │
│                                         │
│  Segment 2 (16 MB)                      │
│  └─ ...                                 │
└─────────────────────────────────────────┘

索引项(Index Entry)结构:
struct IndexEntry {
    uint64_t seq_id;        // 序列号
    uint32_t msg_id_hash;   // 消息 ID 哈希
    uint8_t  status;        // 状态(0=发布, 1=投递, 2=确认)
    uint64_t offset;        // 消息在文件中的偏移量
    uint32_t size;          // 消息大小
    uint8_t  flags;         // 标志位
};

6.5 消息存储文件结构

复制代码
消息存储文件(.ets):

┌─────────────────────────────────────────┐
│      Message Store File (Segment)       │
├─────────────────────────────────────────┤
│  File Header                            │
│  ├─ Magic Number: 0x524D5153 (RMQS)     │
│  ├─ Version: 1                          │
│  ├─ Segment ID: 0                       │
│  └─ Timestamp: ...                      │
├─────────────────────────────────────────┤
│  Message 1                              │
│  ├─ Message Header                      │
│  │   ├─ Total Size: 1024                │
│  │   ├─ Message ID: abc123              │
│  │   ├─ Exchange: order.exchange        │
│  │   ├─ Routing Key: order.created      │
│  │   └─ Properties: {...}               │
│  │                                       │
│  ├─ Message Body                        │
│  │   └─ Actual payload bytes            │
│  └─ Checksum: CRC32                     │
├─────────────────────────────────────────┤
│  Message 2                              │
│  └─ ...                                 │
└─────────────────────────────────────────┘

文件特性:
├─ 每个 Segment 默认 16MB
├─ 顺序写入(高效)
├─ 批量刷盘(减少 I/O)
└─ 定期合并(垃圾回收)

七、工作模式详解

7.1 简单模式(Simple Queue)

复制代码
简单模式:

Producer ─────> [Queue] ─────> Consumer

特点:
├─ 一对一
├─ 最简单的模式
└─ 不使用交换机(使用默认交换机)

场景:
└─ 简单的任务队列

代码示例

java 复制代码
// 生产者
channel.queueDeclare("simple.queue", true, false, false, null);
channel.basicPublish("", "simple.queue", null, message.getBytes());

// 消费者
channel.basicConsume("simple.queue", true, (tag, message) -> {
    System.out.println("收到消息: " + new String(message.getBody()));
});

7.2 工作队列模式(Work Queue)

复制代码
工作队列模式:

                  ┌──> Consumer 1
                  │
Producer ─> [Queue]──> Consumer 2
                  │
                  └──> Consumer 3

特点:
├─ 一对多
├─ 轮询分发(Round-Robin)
├─ 任务分配
└─ 提高处理能力

场景:
├─ 任务分发
└─ 负载均衡

消息分发:
├─ 默认:轮询(每个消费者均匀分配)
└─ 公平分发:prefetchCount=1(按能力分配)

代码示例

java 复制代码
// 生产者发送 10 条消息
for (int i = 1; i <= 10; i++) {
    channel.basicPublish("", "work.queue", null, ("Task " + i).getBytes());
}

// 消费者 1(慢)
channel.basicQos(1);  // 公平分发
channel.basicConsume("work.queue", false, (tag, message) -> {
    Thread.sleep(2000);  // 模拟慢处理
    System.out.println("消费者1: " + new String(message.getBody()));
    channel.basicAck(message.getEnvelope().getDeliveryTag(), false);
});

// 消费者 2(快)
channel.basicQos(1);
channel.basicConsume("work.queue", false, (tag, message) -> {
    Thread.sleep(500);  // 模拟快处理
    System.out.println("消费者2: " + new String(message.getBody()));
    channel.basicAck(message.getEnvelope().getDeliveryTag(), false);
});

// 结果:
// 消费者2处理更多消息(公平分发)

7.3 发布/订阅模式(Publish/Subscribe)

复制代码
发布/订阅模式(Fanout Exchange):

                     ┌──> [Queue 1] ──> Consumer 1
                     │
Producer ──> [Fanout]──> [Queue 2] ──> Consumer 2
            Exchange  │
                     └──> [Queue 3] ──> Consumer 3

特点:
├─ 广播消息
├─ 所有队列都收到消息
└─ 忽略 routing key

场景:
├─ 消息广播
└─ 多系统通知

代码示例

java 复制代码
// 声明交换机
channel.exchangeDeclare("logs.fanout", BuiltinExchangeType.FANOUT, true);

// 生产者发送消息
channel.basicPublish("logs.fanout", "", null, "日志消息".getBytes());

// 消费者 1(日志存储)
String queue1 = channel.queueDeclare().getQueue();  // 临时队列
channel.queueBind(queue1, "logs.fanout", "");
channel.basicConsume(queue1, true, (tag, msg) -> {
    System.out.println("存储日志: " + new String(msg.getBody()));
});

// 消费者 2(日志打印)
String queue2 = channel.queueDeclare().getQueue();
channel.queueBind(queue2, "logs.fanout", "");
channel.basicConsume(queue2, true, (tag, msg) -> {
    System.out.println("打印日志: " + new String(msg.getBody()));
});

7.4 路由模式(Routing)

复制代码
路由模式(Direct Exchange):

                          routing key: error
                     ┌──────────────────────> [Error Queue] ──> Consumer 1
                     │
                     │  routing key: warning
Producer ──> [Direct]──────────────────────> [Warning Queue] ──> Consumer 2
            Exchange │
                     │  routing key: info
                     └──────────────────────> [Info Queue] ──> Consumer 3

特点:
├─ 精确匹配 routing key
├─ 选择性接收消息
└─ 点对点路由

场景:
├─ 日志分级
└─ 任务分类

代码示例

java 复制代码
// 声明交换机
channel.exchangeDeclare("logs.direct", BuiltinExchangeType.DIRECT, true);

// 生产者发送不同级别日志
channel.basicPublish("logs.direct", "error", null, "错误日志".getBytes());
channel.basicPublish("logs.direct", "warning", null, "警告日志".getBytes());
channel.basicPublish("logs.direct", "info", null, "信息日志".getBytes());

// 消费者 1(只接收 error)
channel.queueBind("error.queue", "logs.direct", "error");
channel.basicConsume("error.queue", true, (tag, msg) -> {
    System.out.println("错误: " + new String(msg.getBody()));
});

// 消费者 2(接收 error 和 warning)
channel.queueBind("important.queue", "logs.direct", "error");
channel.queueBind("important.queue", "logs.direct", "warning");
channel.basicConsume("important.queue", true, (tag, msg) -> {
    System.out.println("重要: " + new String(msg.getBody()));
});

7.5 主题模式(Topic)

复制代码
主题模式(Topic Exchange):

                          pattern: *.error.*
                     ┌──────────────────────> [Error Queue]
                     │
                     │  pattern: order.#
Producer ──> [Topic]─┼──────────────────────> [Order Queue]
            Exchange │
                     │  pattern: #.warning
                     └──────────────────────> [Warning Queue]

特点:
├─ 模式匹配 routing key
├─ * 匹配一个单词
├─ # 匹配零个或多个单词
└─ 灵活路由

场景:
├─ 复杂路由
└─ 多维度分类

代码示例

java 复制代码
// 声明交换机
channel.exchangeDeclare("logs.topic", BuiltinExchangeType.TOPIC, true);

// 生产者发送消息
channel.basicPublish("logs.topic", "order.created.info", null, "订单创建".getBytes());
channel.basicPublish("logs.topic", "order.paid.warning", null, "订单支付警告".getBytes());
channel.basicPublish("logs.topic", "user.login.error", null, "用户登录错误".getBytes());

// 消费者 1(所有订单消息:order.#)
channel.queueBind("order.queue", "logs.topic", "order.#");
// 接收:order.created.info, order.paid.warning

// 消费者 2(所有错误消息:*.*.error)
channel.queueBind("error.queue", "logs.topic", "*.*.error");
// 接收:user.login.error

// 消费者 3(所有警告消息:#.warning)
channel.queueBind("warning.queue", "logs.topic", "#.warning");
// 接收:order.paid.warning

7.6 RPC 模式

复制代码
RPC 模式(请求/响应):

Client ──[1] Request──────> [Request Queue] ──> Server
                                                   │
Client <─[4] Response─────< [Reply Queue] <─[3] Process
  │                                                │
  └────[2] Wait──────────────────────────────────[3]

特点:
├─ 同步调用
├─ 客户端等待响应
├─ 使用 correlation_id 关联请求和响应
└─ 使用 reply_to 指定响应队列

流程:
1. 客户端发送请求(指定 reply_to 和 correlation_id)
2. 客户端监听响应队列
3. 服务端处理请求,发送响应到 reply_to
4. 客户端接收响应(匹配 correlation_id)

代码示例

java 复制代码
// RPC 客户端
public String call(String message) throws Exception {
    // 1. 生成关联 ID
    String corrId = UUID.randomUUID().toString();

    // 2. 创建临时响应队列
    String replyQueue = channel.queueDeclare().getQueue();

    // 3. 发送请求
    AMQP.BasicProperties props = new AMQP.BasicProperties.Builder()
        .correlationId(corrId)
        .replyTo(replyQueue)
        .build();
    channel.basicPublish("", "rpc.queue", props, message.getBytes());

    // 4. 等待响应
    CompletableFuture<String> response = new CompletableFuture<>();
    channel.basicConsume(replyQueue, true, (tag, msg) -> {
        if (msg.getProperties().getCorrelationId().equals(corrId)) {
            response.complete(new String(msg.getBody()));
        }
    });

    return response.get(5, TimeUnit.SECONDS);
}

// RPC 服务端
channel.basicConsume("rpc.queue", false, (tag, msg) -> {
    // 1. 处理请求
    String request = new String(msg.getBody());
    String result = process(request);

    // 2. 发送响应
    AMQP.BasicProperties props = new AMQP.BasicProperties.Builder()
        .correlationId(msg.getProperties().getCorrelationId())
        .build();
    channel.basicPublish("", msg.getProperties().getReplyTo(), props, result.getBytes());

    // 3. 确认消息
    channel.basicAck(msg.getEnvelope().getDeliveryTag(), false);
});

八、使用指南

8.1 Java 原生客户端

8.1.1 Maven 依赖
xml 复制代码
<dependency>
    <groupId>com.rabbitmq</groupId>
    <artifactId>amqp-client</artifactId>
    <version>5.20.0</version>
</dependency>
8.1.2 生产者示例
java 复制代码
import com.rabbitmq.client.*;

public class Producer {

    private static final String EXCHANGE_NAME = "order.exchange";
    private static final String ROUTING_KEY = "order.created";

    public static void main(String[] args) throws Exception {
        // 1. 创建连接工厂
        ConnectionFactory factory = new ConnectionFactory();
        factory.setHost("localhost");
        factory.setPort(5672);
        factory.setUsername("guest");
        factory.setPassword("guest");
        factory.setVirtualHost("/");

        // 2. 创建连接
        try (Connection connection = factory.newConnection();
             Channel channel = connection.createChannel()) {

            // 3. 声明交换机
            channel.exchangeDeclare(
                EXCHANGE_NAME,
                BuiltinExchangeType.DIRECT,
                true,   // durable
                false,  // autoDelete
                null    // arguments
            );

            // 4. 声明队列
            channel.queueDeclare(
                "order.queue",
                true,   // durable
                false,  // exclusive
                false,  // autoDelete
                null    // arguments
            );

            // 5. 绑定队列
            channel.queueBind("order.queue", EXCHANGE_NAME, ROUTING_KEY);

            // 6. 发送消息
            String message = "订单创建: Order-12345";
            AMQP.BasicProperties props = new AMQP.BasicProperties.Builder()
                .contentType("text/plain")
                .deliveryMode(2)  // 持久化
                .priority(5)
                .timestamp(new Date())
                .build();

            channel.basicPublish(EXCHANGE_NAME, ROUTING_KEY, props, message.getBytes());
            System.out.println("发送消息: " + message);
        }
    }
}
8.1.3 消费者示例
java 复制代码
public class Consumer {

    private static final String QUEUE_NAME = "order.queue";

    public static void main(String[] args) throws Exception {
        ConnectionFactory factory = new ConnectionFactory();
        factory.setHost("localhost");

        Connection connection = factory.newConnection();
        Channel channel = connection.createChannel();

        // 设置 QoS(每次只取1条消息)
        channel.basicQos(1);

        // 消费消息(手动确认)
        DeliverCallback deliverCallback = (consumerTag, message) -> {
            try {
                String msg = new String(message.getBody(), "UTF-8");
                System.out.println("收到消息: " + msg);

                // 模拟业务处理
                Thread.sleep(1000);

                // 手动确认
                channel.basicAck(message.getEnvelope().getDeliveryTag(), false);
            } catch (Exception e) {
                // 拒绝消息,重新入队
                channel.basicNack(
                    message.getEnvelope().getDeliveryTag(),
                    false,  // multiple
                    true    // requeue
                );
            }
        };

        channel.basicConsume(QUEUE_NAME, false, deliverCallback, consumerTag -> {});

        System.out.println("等待消息...");
    }
}

8.2 Spring Boot 集成

8.2.1 Maven 依赖
xml 复制代码
<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-amqp</artifactId>
</dependency>
8.2.2 配置文件
yaml 复制代码
# application.yml
spring:
  rabbitmq:
    host: localhost
    port: 5672
    username: guest
    password: guest
    virtual-host: /
    # 发布者确认
    publisher-confirm-type: correlated
    publisher-returns: true
    # 消费者配置
    listener:
      simple:
        acknowledge-mode: manual  # 手动确认
        prefetch: 1  # 预取数量
        retry:
          enabled: true
          max-attempts: 3
8.2.3 配置类
java 复制代码
@Configuration
public class RabbitMQConfig {

    // 声明交换机
    @Bean
    public DirectExchange orderExchange() {
        return new DirectExchange("order.exchange", true, false);
    }

    // 声明队列
    @Bean
    public Queue orderQueue() {
        return QueueBuilder.durable("order.queue")
            .ttl(60000)  // 消息 TTL
            .maxLength(10000)  // 最大长度
            .deadLetterExchange("dlx.exchange")  // 死信交换机
            .build();
    }

    // 绑定
    @Bean
    public Binding orderBinding(Queue orderQueue, DirectExchange orderExchange) {
        return BindingBuilder.bind(orderQueue)
            .to(orderExchange)
            .with("order.created");
    }

    // RabbitTemplate 配置
    @Bean
    public RabbitTemplate rabbitTemplate(ConnectionFactory connectionFactory) {
        RabbitTemplate template = new RabbitTemplate(connectionFactory);

        // 消息发送确认
        template.setConfirmCallback((correlationData, ack, cause) -> {
            if (ack) {
                System.out.println("消息发送成功");
            } else {
                System.err.println("消息发送失败: " + cause);
            }
        });

        // 消息返回(路由失败)
        template.setReturnsCallback(returned -> {
            System.err.println("消息路由失败: " + returned.getMessage());
        });

        return template;
    }
}
8.2.4 生产者
java 复制代码
@Service
public class OrderService {

    @Autowired
    private RabbitTemplate rabbitTemplate;

    public void createOrder(Order order) {
        // 1. 保存订单
        saveOrder(order);

        // 2. 发送消息
        rabbitTemplate.convertAndSend(
            "order.exchange",
            "order.created",
            order,
            message -> {
                // 设置消息属性
                message.getMessageProperties().setContentType("application/json");
                message.getMessageProperties().setPriority(5);
                return message;
            }
        );
    }
}
8.2.5 消费者
java 复制代码
@Component
public class OrderListener {

    @RabbitListener(queues = "order.queue")
    public void handleOrder(Order order, Message message, Channel channel) throws Exception {
        long deliveryTag = message.getMessageProperties().getDeliveryTag();

        try {
            // 业务处理
            System.out.println("处理订单: " + order);

            // 模拟处理
            processOrder(order);

            // 手动确认
            channel.basicAck(deliveryTag, false);

        } catch (Exception e) {
            // 拒绝消息,重新入队
            channel.basicNack(deliveryTag, false, true);
        }
    }

    // 使用注解声明队列和绑定
    @RabbitListener(bindings = @QueueBinding(
        value = @Queue(value = "payment.queue", durable = "true"),
        exchange = @Exchange(value = "order.exchange", type = "direct"),
        key = "order.paid"
    ))
    public void handlePayment(Order order) {
        System.out.println("处理支付: " + order);
    }
}

九、高级特性

9.1 消息可靠性保证

9.1.1 生产者确认
java 复制代码
// 1. 启用发布者确认
channel.confirmSelect();

// 2. 发送消息
channel.basicPublish(exchange, routingKey, props, body);

// 3. 等待确认
if (channel.waitForConfirms()) {
    System.out.println("消息发送成功");
} else {
    System.out.println("消息发送失败");
}

// 批量确认
for (int i = 0; i < 100; i++) {
    channel.basicPublish(exchange, routingKey, props, body);
}
channel.waitForConfirmsOrDie(5000);  // 等待所有确认

// 异步确认(推荐)
channel.addConfirmListener(new ConfirmListener() {
    @Override
    public void handleAck(long deliveryTag, boolean multiple) {
        System.out.println("消息确认: " + deliveryTag);
    }

    @Override
    public void handleNack(long deliveryTag, boolean multiple) {
        System.out.println("消息拒绝: " + deliveryTag);
        // 重发消息
    }
});
9.1.2 消息持久化
java 复制代码
// 1. 交换机持久化
channel.exchangeDeclare(exchange, type, true);  // durable=true

// 2. 队列持久化
channel.queueDeclare(queue, true, false, false, null);  // durable=true

// 3. 消息持久化
AMQP.BasicProperties props = MessageProperties.PERSISTENT_TEXT_PLAIN;
// 或
AMQP.BasicProperties props = new AMQP.BasicProperties.Builder()
    .deliveryMode(2)  // 2=持久化
    .build();

channel.basicPublish(exchange, routingKey, props, body);
9.1.3 消息补偿机制
java 复制代码
@Service
public class MessageService {

    @Autowired
    private RabbitTemplate rabbitTemplate;

    @Autowired
    private MessageRepository messageRepository;

    // 发送消息(带补偿)
    public void sendMessage(String content) {
        // 1. 保存消息到数据库
        Message msg = new Message();
        msg.setContent(content);
        msg.setStatus("PENDING");
        messageRepository.save(msg);

        try {
            // 2. 发送消息
            rabbitTemplate.convertAndSend("exchange", "routingKey", content);

            // 3. 标记为已发送
            msg.setStatus("SENT");
            messageRepository.save(msg);

        } catch (Exception e) {
            // 4. 标记为失败
            msg.setStatus("FAILED");
            messageRepository.save(msg);
        }
    }

    // 定时补偿
    @Scheduled(fixedDelay = 60000)
    public void retryFailedMessages() {
        List<Message> failed = messageRepository.findByStatus("FAILED");
        for (Message msg : failed) {
            try {
                rabbitTemplate.convertAndSend("exchange", "routingKey", msg.getContent());
                msg.setStatus("SENT");
                messageRepository.save(msg);
            } catch (Exception e) {
                // 继续失败
            }
        }
    }
}

9.2 死信队列(DLX)

复制代码
死信队列场景:

1. 消息被拒绝(basic.reject / basic.nack)且 requeue=false
2. 消息 TTL 过期
3. 队列达到最大长度

死信队列架构:

正常流程:
Producer ──> [Normal Exchange] ──> [Normal Queue] ──> Consumer
                                          │
                                     消息变成死信
                                          │
死信流程:                                 ↓
                                [Dead Letter Exchange]
                                          │
                                          ↓
                                   [Dead Letter Queue] ──> DLX Consumer

配置示例

java 复制代码
@Configuration
public class DLXConfig {

    // 1. 死信交换机
    @Bean
    public DirectExchange dlxExchange() {
        return new DirectExchange("dlx.exchange", true, false);
    }

    // 2. 死信队列
    @Bean
    public Queue dlxQueue() {
        return new Queue("dlx.queue", true);
    }

    // 3. 死信绑定
    @Bean
    public Binding dlxBinding() {
        return BindingBuilder.bind(dlxQueue())
            .to(dlxExchange())
            .with("dlx");
    }

    // 4. 正常队列(配置死信交换机)
    @Bean
    public Queue normalQueue() {
        return QueueBuilder.durable("normal.queue")
            .deadLetterExchange("dlx.exchange")  // 指定死信交换机
            .deadLetterRoutingKey("dlx")         // 死信路由键
            .ttl(60000)  // 消息 TTL 60 秒
            .maxLength(1000)  // 最大长度
            .build();
    }
}

死信消费者

java 复制代码
@Component
public class DLXListener {

    @RabbitListener(queues = "dlx.queue")
    public void handleDeadLetter(Message message) {
        // 分析死信原因
        Map<String, Object> headers = message.getMessageProperties().getHeaders();
        String reason = (String) headers.get("x-first-death-reason");

        System.out.println("死信原因: " + reason);
        System.out.println("原交换机: " + headers.get("x-first-death-exchange"));
        System.out.println("原路由键: " + headers.get("x-first-death-queue"));

        // 记录日志、告警、补偿处理
    }
}

9.3 延迟队列

复制代码
延迟队列实现方案:

方案1:TTL + 死信队列
┌──────────────────────────────────────────────┐
│  Producer ──> [TTL Queue] ──TTL过期──> [DLX] │
│                 (无消费者)                   │
│                    │                         │
│                    └──> [Delay Queue] ──> Consumer
└──────────────────────────────────────────────┘

方案2:延迟队列插件(推荐)
└─ rabbitmq-delayed-message-exchange 插件

方案1:TTL + 死信队列

java 复制代码
@Configuration
public class DelayQueueConfig {

    // 1. 延迟交换机
    @Bean
    public DirectExchange delayExchange() {
        return new DirectExchange("delay.exchange");
    }

    // 2. TTL 队列(无消费者,消息过期后进入死信队列)
    @Bean
    public Queue ttlQueue() {
        return QueueBuilder.durable("ttl.queue")
            .ttl(30000)  // 30 秒后过期
            .deadLetterExchange("real.exchange")
            .deadLetterRoutingKey("real")
            .build();
    }

    // 3. 真正的队列
    @Bean
    public Queue realQueue() {
        return new Queue("real.queue", true);
    }

    @Bean
    public DirectExchange realExchange() {
        return new DirectExchange("real.exchange");
    }

    @Bean
    public Binding realBinding() {
        return BindingBuilder.bind(realQueue())
            .to(realExchange())
            .with("real");
    }
}

// 发送延迟消息
rabbitTemplate.convertAndSend("delay.exchange", "delay", "延迟30秒的消息");

// 30秒后,消息自动到达 real.queue
@RabbitListener(queues = "real.queue")
public void handle(String message) {
    System.out.println("收到延迟消息: " + message);
}

方案2:延迟队列插件

bash 复制代码
# 安装插件
rabbitmq-plugins enable rabbitmq_delayed_message_exchange
java 复制代码
@Bean
public CustomExchange delayExchange() {
    Map<String, Object> args = new HashMap<>();
    args.put("x-delayed-type", "direct");
    return new CustomExchange(
        "delayed.exchange",
        "x-delayed-message",  // 类型
        true,
        false,
        args
    );
}

// 发送延迟消息
rabbitTemplate.convertAndSend("delayed.exchange", "delayed", "消息", message -> {
    message.getMessageProperties().setDelay(30000);  // 延迟 30 秒
    return message;
});

9.4 优先级队列

java 复制代码
@Bean
public Queue priorityQueue() {
    return QueueBuilder.durable("priority.queue")
        .maxPriority(10)  // 最大优先级 10
        .build();
}

// 发送高优先级消息
rabbitTemplate.convertAndSend("exchange", "priority", "重要消息", message -> {
    message.getMessageProperties().setPriority(9);
    return message;
});

// 发送低优先级消息
rabbitTemplate.convertAndSend("exchange", "priority", "普通消息", message -> {
    message.getMessageProperties().setPriority(1);
    return message;
});

// 消费顺序:优先级高的先消费

9.5 集群与高可用

9.5.1 集群模式
复制代码
RabbitMQ 集群架构:

┌──────────────┐   ┌──────────────┐   ┌──────────────┐
│   Node 1     │   │   Node 2     │   │   Node 3     │
│              │   │              │   │              │
│  ┌────────┐  │   │  ┌────────┐  │   │  ┌────────┐  │
│  │ Queue  │  │   │  │ Queue  │  │   │  │ Queue  │  │
│  │   A    │  │   │  │   B    │  │   │  │   C    │  │
│  └────────┘  │   │  └────────┘  │   │  └────────┘  │
│              │   │              │   │              │
│  Metadata    │───│  Metadata    │───│  Metadata    │
│  (Mnesia)    │   │  (Mnesia)    │   │  (Mnesia)    │
└──────────────┘   └──────────────┘   └──────────────┘

特点:
├─ 元数据共享(Mnesia 数据库同步)
├─ 队列不共享(每个队列在一个节点)
└─ 消息不复制(普通集群)

搭建集群

bash 复制代码
# 节点 1
rabbitmqctl stop_app
rabbitmqctl reset
rabbitmqctl start_app

# 节点 2(加入节点 1)
rabbitmqctl stop_app
rabbitmqctl reset
rabbitmqctl join_cluster rabbit@node1
rabbitmqctl start_app

# 节点 3(加入节点 1)
rabbitmqctl stop_app
rabbitmqctl reset
rabbitmqctl join_cluster rabbit@node1
rabbitmqctl start_app

# 查看集群状态
rabbitmqctl cluster_status
9.5.2 镜像队列(高可用)
复制代码
镜像队列架构:

┌──────────────┐   ┌──────────────┐   ┌──────────────┐
│   Master     │   │   Mirror 1   │   │   Mirror 2   │
│   Node 1     │   │   Node 2     │   │   Node 3     │
│              │   │              │   │              │
│  ┌────────┐  │   │  ┌────────┐  │   │  ┌────────┐  │
│  │ Queue  │──┼───┼─>│ Queue  │──┼───┼─>│ Queue  │  │
│  │   A    │<─┼───┼──│   A'   │<─┼───┼──│   A''  │  │
│  │(Master)│  │   │  │(Mirror)│  │   │  │(Mirror)│  │
│  └────────┘  │   │  └────────┘  │   │  └────────┘  │
└──────────────┘   └──────────────┘   └──────────────┘

特点:
├─ 消息自动同步到镜像节点
├─ Master 故障,Mirror 自动升级
└─ 保证消息不丢失

配置镜像队列

bash 复制代码
# 方式1:命令行配置 Policy
rabbitmqctl set_policy ha-all "^ha\." \
  '{"ha-mode":"all","ha-sync-mode":"automatic"}'

# 参数说明:
# ha-mode:
#   - all:镜像到所有节点
#   - exactly:镜像到指定数量节点 {"ha-mode":"exactly","ha-params":2}
#   - nodes:镜像到指定节点 {"ha-mode":"nodes","ha-params":["rabbit@node1","rabbit@node2"]}
# ha-sync-mode:
#   - manual:手动同步
#   - automatic:自动同步(推荐)
java 复制代码
// 方式2:Java 代码配置
@Bean
public Queue haQueue() {
    return QueueBuilder.durable("ha.queue")
        .build();
}

// 在 RabbitMQ 管理界面配置 Policy
// Pattern: ^ha\.
// Apply to: queues
// Definition: {"ha-mode":"all","ha-sync-mode":"automatic"}

十、性能优化与最佳实践

10.1 性能优化

10.1.1 批量发送
java 复制代码
// ❌ 不推荐:逐条发送
for (int i = 0; i < 10000; i++) {
    rabbitTemplate.convertAndSend("exchange", "key", "消息" + i);
}

// ✅ 推荐:批量发送
List<Message> messages = new ArrayList<>();
for (int i = 0; i < 10000; i++) {
    messages.add(new Message(("消息" + i).getBytes()));
}

// 分批发送(每批 100 条)
for (int i = 0; i < messages.size(); i += 100) {
    List<Message> batch = messages.subList(i, Math.min(i + 100, messages.size()));
    // 批量发送
}
10.1.2 连接和信道复用
java 复制代码
// ❌ 不推荐:每次创建新连接
public void send() throws Exception {
    Connection conn = factory.newConnection();
    Channel channel = conn.createChannel();
    channel.basicPublish(...);
    channel.close();
    conn.close();  // 频繁创建/关闭连接,性能差
}

// ✅ 推荐:复用连接和信道
private static Connection connection;
private static ThreadLocal<Channel> channelThreadLocal = new ThreadLocal<>();

static {
    try {
        connection = factory.newConnection();
    } catch (Exception e) {
        e.printStackTrace();
    }
}

public void send() throws Exception {
    Channel channel = channelThreadLocal.get();
    if (channel == null || !channel.isOpen()) {
        channel = connection.createChannel();
        channelThreadLocal.set(channel);
    }
    channel.basicPublish(...);
}
10.1.3 预取数量(prefetch)
java 复制代码
// 设置预取数量
channel.basicQos(10);  // 每次预取 10 条消息

// 场景:
// prefetch=1:公平分发,适合处理时间不均匀的任务
// prefetch=10-50:提高吞吐量,适合处理时间均匀的任务
// prefetch=0:无限制(不推荐)
10.1.4 消息压缩
java 复制代码
// 发送前压缩
public void sendCompressed(String message) throws Exception {
    byte[] compressed = compress(message.getBytes());
    rabbitTemplate.convertAndSend("exchange", "key", compressed);
}

// 接收后解压
@RabbitListener(queues = "queue")
public void receive(byte[] compressed) throws Exception {
    String message = new String(decompress(compressed));
    System.out.println(message);
}

private byte[] compress(byte[] data) throws Exception {
    ByteArrayOutputStream baos = new ByteArrayOutputStream();
    GZIPOutputStream gzip = new GZIPOutputStream(baos);
    gzip.write(data);
    gzip.close();
    return baos.toByteArray();
}

private byte[] decompress(byte[] data) throws Exception {
    ByteArrayInputStream bais = new ByteArrayInputStream(data);
    GZIPInputStream gzip = new GZIPInputStream(bais);
    ByteArrayOutputStream baos = new ByteArrayOutputStream();
    byte[] buffer = new byte[1024];
    int len;
    while ((len = gzip.read(buffer)) > 0) {
        baos.write(buffer, 0, len);
    }
    gzip.close();
    return baos.toByteArray();
}

10.2 最佳实践

10.2.1 命名规范
复制代码
交换机命名:
├─ {业务}.{类型}.exchange
└─ 示例:order.direct.exchange、user.topic.exchange

队列命名:
├─ {业务}.{功能}.queue
└─ 示例:order.create.queue、user.register.queue

路由键命名:
├─ {业务}.{动作}.{级别}
└─ 示例:order.created.info、user.login.error
10.2.2 消息幂等性
java 复制代码
// 方案1:消息 ID 去重
@Service
public class MessageService {

    @Autowired
    private RedisTemplate<String, String> redisTemplate;

    @RabbitListener(queues = "order.queue")
    public void handleOrder(Order order, Message message) {
        String messageId = message.getMessageProperties().getMessageId();

        // 检查是否已处理
        Boolean exists = redisTemplate.opsForValue()
            .setIfAbsent("msg:" + messageId, "1", 24, TimeUnit.HOURS);

        if (Boolean.TRUE.equals(exists)) {
            // 第一次处理
            processOrder(order);
        } else {
            // 重复消息,忽略
            System.out.println("重复消息: " + messageId);
        }
    }
}

// 方案2:业务唯一键去重
@RabbitListener(queues = "order.queue")
public void handleOrder(Order order) {
    try {
        // 使用数据库唯一索引保证幂等
        orderRepository.insert(order);  // order_id 唯一索引
    } catch (DuplicateKeyException e) {
        // 重复订单,忽略
        System.out.println("重复订单: " + order.getId());
    }
}
10.2.3 监控告警
yaml 复制代码
# application.yml
management:
  endpoints:
    web:
      exposure:
        include: '*'
  metrics:
    export:
      prometheus:
        enabled: true

spring:
  rabbitmq:
    listener:
      simple:
        retry:
          enabled: true
          max-attempts: 3
          initial-interval: 1000
java 复制代码
// 自定义监控
@Component
public class RabbitMQMonitor {

    @Autowired
    private RabbitTemplate rabbitTemplate;

    @Scheduled(fixedDelay = 60000)
    public void monitorQueues() {
        Properties props = rabbitTemplate.execute(channel -> {
            AMQP.Queue.DeclareOk ok = channel.queueDeclarePassive("order.queue");
            Properties p = new Properties();
            p.setProperty("messageCount", String.valueOf(ok.getMessageCount()));
            p.setProperty("consumerCount", String.valueOf(ok.getConsumerCount()));
            return p;
        });

        int messageCount = Integer.parseInt(props.getProperty("messageCount"));
        if (messageCount > 10000) {
            // 告警:消息积压
            alert("队列消息积压: " + messageCount);
        }
    }
}
10.2.4 消息顺序性
复制代码
保证消息顺序的方案:

方案1:单队列 + 单消费者
Producer ──> [Queue] ──> Consumer (prefetch=1)
问题:吞吐量低

方案2:分区队列
Producer ──hash(orderId)──> [Queue 1] ──> Consumer 1
                      ├──> [Queue 2] ──> Consumer 2
                      └──> [Queue 3] ──> Consumer 3
好处:同一 orderId 的消息进入同一队列,保证顺序

方案3:业务层保证
├─ 使用版本号
└─ 使用时间戳
java 复制代码
// 方案2:分区队列示例
public void sendOrder(Order order) {
    // 根据订单 ID 哈希,选择队列
    int partition = Math.abs(order.getId().hashCode() % 3);
    String queue = "order.queue." + partition;

    rabbitTemplate.convertAndSend("order.exchange", queue, order);
}

// 每个队列一个消费者
@RabbitListener(queues = "order.queue.0", concurrency = "1")
public void handle0(Order order) { ... }

@RabbitListener(queues = "order.queue.1", concurrency = "1")
public void handle1(Order order) { ... }

@RabbitListener(queues = "order.queue.2", concurrency = "1")
public void handle2(Order order) { ... }
相关推荐
luod2 小时前
RabbitMQ工作队列模式理解
rabbitmq
BullSmall3 小时前
JDK17下Kafka部署全指南
分布式·kafka
悟空码字4 小时前
SpringBoot 整合 RabbitMQ:和这只“兔子”交朋友
java·后端·rabbitmq
BullSmall5 小时前
MinIO分布式存储实战指南
分布式
回家路上绕了弯5 小时前
数据模型设计实战指南:从业务到落地的全流程方法论
分布式·后端
吃好喝好玩好睡好5 小时前
OpenHarmony 分布式环境下 Electron+Flutter 应用的增量更新设计
分布式·flutter·eclipse·electron
西***63475 小时前
人机分离・全域互联!分布式 KVM 坐席系统:应急指挥与多领域管控的硬核支撑
分布式
爬山算法5 小时前
Redis(170)如何使用Redis实现分布式限流?
数据库·redis·分布式
Macbethad5 小时前
WPF工业设备远程控制程序技术方案
分布式·wpf