消息队列高阶应用 - 从原理到实战全面解析

0 阅读40分钟

一、历史背景与发展历程

1.1 消息队列诞生的历史背景

分布式系统的通信困境

早期单体应用的问题:

传统单体应用架构:
- 所有功能模块在同一个进程中
- 模块间直接调用函数
- 数据库是唯一的外部依赖

问题:
- 耦合度高:一个模块的修改可能影响整个系统
- 扩展性差:无法针对单个模块扩容
- 可用性低:任何一个模块崩溃,整个系统不可用
- 开发效率低:团队协作困难,部署复杂

微服务架构的兴起:

2010年代,微服务架构逐渐流行:
- 按业务功能拆分为独立服务
- 服务间通过网络通信
- 每个服务独立部署、独立扩展

新的挑战:
- 服务间如何可靠通信?
- 如何应对服务暂时不可用?
- 如何处理流量峰值?
- 如何保证数据最终一致性?

同步调用的局限性:

HTTP/RPC 同步调用的问题:

场景:电商下单流程
1. 订单服务 → 库存服务(扣减库存)
2. 订单服务 → 支付服务(创建支付单)
3. 订单服务 → 物流服务(创建物流单)
4. 订单服务 → 积分服务(赠送积分)
5. 订单服务 → 通知服务(发送短信/邮件)

问题:
- 串行调用,响应时间累加(如果每个服务200ms,总共1秒)
- 任何一个服务故障,整个下单失败
- 流量峰值时,所有服务都承受压力
- 服务间紧耦合,修改一个服务可能影响其他服务

消息队列的解决方案

核心理念:异步解耦:

使用消息队列重构下单流程:

1. 订单服务:
   - 创建订单(持久化到数据库)
   - 发送"订单创建"消息到MQ
   - 立即返回成功(响应时间:50ms)

2. 后续处理(异步):
   - 库存服务:订阅消息,扣减库存
   - 支付服务:订阅消息,创建支付单
   - 物流服务:订阅消息,创建物流单
   - 积分服务:订阅消息,赠送积分
   - 通知服务:订阅消息,发送通知

优势:
- 快速响应:用户不需要等待所有服务处理完成
- 高可用:任何一个服务故障,不影响订单创建
- 削峰填谷:MQ缓冲流量峰值,服务按自己的速度消费
- 解耦:服务间无需直接依赖,通过消息通信

1.2 消息队列解决的核心问题

问题1:系统解耦

紧耦合架构的维护噩梦:

场景:用户注册后的后续操作

紧耦合方式:
UserService.register(user) {
    userRepository.save(user);
    emailService.sendWelcomeEmail(user);      // 发送欢迎邮件
    smsService.sendVerificationCode(user);    // 发送验证码
    pointsService.grantPoints(user, 100);     // 赠送积分
    crmService.syncUserInfo(user);            // 同步到CRM系统
    analyticsService.trackRegistration(user); // 数据分析
}

问题:
1. 新增功能需要修改UserService代码
   - 例如:加入会员等级系统,需要修改register方法
2. 任何一个服务异常,注册流程失败
   - 例如:CRM系统维护,导致无法注册新用户
3. 性能受制于最慢的服务
   - 如果Analytics服务响应慢,用户注册变慢
4. 测试复杂:需要准备所有依赖服务的测试环境

消息队列解耦方案:

UserService.register(user) {
    userRepository.save(user);
    messageQueue.publish("user.registered", user);  // 只需发送消息
}

// 各个服务独立订阅
EmailService:   订阅 "user.registered" → 发送欢迎邮件
SmsService:     订阅 "user.registered" → 发送验证码
PointsService:  订阅 "user.registered" → 赠送积分
CrmService:     订阅 "user.registered" → 同步用户信息
AnalyticsService: 订阅 "user.registered" → 记录注册事件

优势:
- 新增功能:只需新增订阅者,无需修改UserService
- 容错性:任何订阅者故障,不影响用户注册
- 性能:用户注册只需等待save + publish,毫秒级响应
- 可测试性:UserService只需测试消息是否正确发送

问题2:异步处理

同步阻塞的性能瓶颈:

场景:视频上传处理

同步方式:
1. 用户上传视频(2GB)
2. 服务端接收上传(耗时:1分钟)
3. 转码为多种格式(耗时:10分钟)
   - 1080P
   - 720P
   - 480P
   - 移动端格式
4. 生成缩略图(耗时:30秒)
5. 提取字幕(耗时:5分钟)
6. AI内容审核(耗时:2分钟)
7. 返回成功

总耗时:18分30秒

用户体验:上传后要等待近20分钟才能看到结果,期间界面卡死

消息队列异步方案:

异步方式:
1. 用户上传视频(2GB)
2. 服务端接收上传并持久化(耗时:1分钟)
3. 发送消息到MQ:
   - "video.uploaded" → 转码队列
   - "video.uploaded" → 缩略图队列
   - "video.uploaded" → 字幕提取队列
   - "video.uploaded" → 审核队列
4. 立即返回:"上传成功,处理中..."

后台异步处理:
- 转码服务:并发处理,10分钟完成
- 缩略图服务:并发处理,30秒完成
- 字幕服务:并发处理,5分钟完成
- 审核服务:并发处理,2分钟完成

用户体验:
- 上传完成后立即得到反馈(1分钟)
- 可以继续浏览其他内容
- 收到推送通知:"您的视频已处理完成"

问题3:流量削峰

流量峰值的系统崩溃风险:

场景:电商促销活动(双11、618)

流量特征:
- 平时:1000 QPS
- 促销开始瞬间:100,000 QPS(100倍)
- 持续高峰:30分钟

传统架构问题:
1. 数据库连接池耗尽:
   - 最大连接数:1000
   - 100,000请求同时到达
   - 99%的请求等待连接超时

2. 服务器资源耗尽:
   - CPU飙升到100%
   - 内存不足导致OOM
   - 服务响应变慢甚至崩溃

3. 雪崩效应:
   - 订单服务崩溃 → 用户疯狂重试
   - 库存服务崩溃 → 订单服务调用失败
   - 整个系统瘫痪

应对方案:
- 扩容10倍服务器(成本高,只用几个小时)
- 限流(用户体验差,大量请求被拒绝)

消息队列削峰填谷:

使用MQ的削峰方案:

流量高峰:
1. 用户点击秒杀(100,000 QPS)
2. 接口快速将请求放入MQ(毫秒级)
3. 返回:"排队中,请稍候..."

后端处理:
- MQ中的消息:100,000条(峰值瞬间堆积)
- 消费者以恒定速率消费:1000 QPS
- 100,000条消息处理完成:约100秒

效果:
- 用户体验:虽然不是秒级响应,但系统稳定,100秒后一定有结果
- 系统稳定:不需要扩容,按平时的容量稳定处理
- 成本优化:无需为短暂峰值投入大量硬件

比喻:
- 没有MQ:水龙头全开,水管爆裂
- 有MQ:水龙头全开,蓄水池缓冲,水管稳定出水

问题4:最终一致性

分布式事务的困境:

场景:电商下单,涉及3个服务

业务要求:
- 订单服务:创建订单
- 库存服务:扣减库存
- 积分服务:扣减积分

要么全部成功,要么全部失败(事务)

分布式事务方案的问题:
1. 两阶段提交(2PC):
   - 性能差:需要等待所有服务
   - 阻塞:协调者故障,所有参与者阻塞
   - 单点故障:协调者是单点

2. 三阶段提交(3PC):
   - 复杂度高
   - 仍有一致性问题

3. TCC(Try-Confirm-Cancel):
   - 编码复杂:需要实现Try、Confirm、Cancel三个接口
   - 业务侵入性强

消息队列最终一致性方案:

基于消息的最终一致性:

1. 订单服务(事务):
   - 创建订单
   - 保存本地消息表:"订单创建"消息
   - 提交数据库事务

2. 定时任务:
   - 扫描本地消息表
   - 发送消息到MQ
   - 更新消息状态为"已发送"

3. 库存服务:
   - 订阅"订单创建"消息
   - 扣减库存
   - 发送ACK

4. 积分服务:
   - 订阅"订单创建"消息
   - 扣减积分
   - 发送ACK

特点:
- 最终一致性:可能有短暂的不一致,但最终会一致
- 高性能:无需同步等待
- 高可用:某个服务故障,消息会重试
- 编码简单:只需发送和接收消息

权衡:
- 优点:性能好、可用性高、编码简单
- 缺点:不是强一致性,有短暂延迟(通常秒级)
- 适用:大部分业务场景(订单、支付、库存等)
- 不适用:金融转账等强一致性场景

1.3 发展历程中的重要里程碑

1980s-1990s:早期消息中间件

IBM MQ(1993年):

  • 全名:IBM MQSeries(后改名为WebSphere MQ、IBM MQ)
  • 第一个商业化的消息队列产品
  • 特点:
    • 基于队列模型(Queue)
    • 支持事务
    • 高可靠性
    • 企业级功能
  • 应用:
    • 银行、证券、电信等对可靠性要求极高的行业
    • 至今仍在使用
  • 问题:
    • 商业软件,授权费用高
    • 配置复杂,学习曲线陡峭
    • 性能不是最优

Microsoft Message Queue (MSMQ, 1997年):

  • Windows平台的消息队列
  • 与Windows系统深度集成
  • 主要用于Windows生态
  • 局限性:跨平台能力弱

2000s:开源消息队列兴起

JMS规范(2001年):

  • Java Message Service
  • Sun公司制定的Java消息服务API规范
  • 定义了消息传递的标准接口
  • 实现:
    • ActiveMQ(Apache,2004年)
    • IBM MQ
    • Oracle AQ

ActiveMQ(2004年):

  • Apache基金会开源项目
  • 第一个流行的开源消息队列
  • 特点:
    • 支持JMS规范
    • 支持多种协议(OpenWire、AMQP、STOMP、MQTT)
    • 支持多种语言客户端
    • 功能丰富
  • 问题:
    • 性能一般
    • 并发能力有限
    • 后期发展缓慢

ZeroMQ(2007年):

  • 轻量级消息库(而非消息队列服务器)
  • 特点:
    • 嵌入式,无需独立部署
    • 极高性能(微秒级延迟)
    • 支持多种消息模式
  • 局限:
    • 无消息持久化
    • 无高可用机制
    • 需要自己实现很多功能

2007-2010:AMQP协议与RabbitMQ

AMQP协议(2007年):

  • Advanced Message Queuing Protocol
  • 由JP Morgan、Cisco等公司联合制定
  • 目标:消息队列的标准协议(类似HTTP之于Web)
  • 特性:
    • 跨语言、跨平台
    • 定义了消息格式、路由规则、可靠性保证
    • 版本:AMQP 0.9.1、AMQP 1.0

RabbitMQ(2007年):

  • 基于Erlang开发
  • 实现AMQP协议
  • 特点:
    • 高可靠性(Erlang天生的容错能力)
    • 灵活的路由(Exchange + Binding)
    • 集群和高可用
    • 管理界面友好
    • 插件机制丰富
  • 应用:
    • 中小规模消息传递
    • 任务队列(Celery等框架默认使用)
    • 微服务间通信
  • 问题:
    • 性能不如Kafka(吞吐量:万级/秒)
    • 不适合大数据场景

2011-2015:大数据时代的消息队列

Kafka(2011年,LinkedIn开源):

  • 为大数据场景设计
  • 特点:
    • 超高吞吐量(百万级/秒)
    • 分布式、可扩展
    • 持久化存储(磁盘)
    • 消息回溯(可重复消费)
  • 设计理念:
    • 消息队列 + 分布式日志
    • 顺序写磁盘(比随机写内存还快)
    • 零拷贝技术
    • 批量处理
  • 应用场景:
    • 日志收集(每天TB级)
    • 实时数据流(Flink、Spark Streaming)
    • 事件溯源
    • CQRS架构
  • 里程碑:
    • 2011:LinkedIn开源
    • 2014:加入Apache基金会
    • 2017:Kafka Streams(流处理)
    • 2019:Kafka成为Apache顶级项目

RocketMQ(2012年,阿里巴巴开源):

  • 参考Kafka设计,针对电商场景优化
  • 特点:
    • 高性能(十万级/秒)
    • 低延迟(毫秒级)
    • 顺序消息
    • 事务消息
    • 延迟消息
  • 应用:
    • 阿里巴巴内部:双11核心链路
    • 适合电商、金融等业务场景
  • 里程碑:
    • 2012:阿里内部开发
    • 2016:开源
    • 2017:加入Apache孵化器
    • 2019:Apache顶级项目

2015-至今:云原生与多样化发展

Pulsar(2016年,Yahoo开源):

  • 下一代消息队列
  • 特点:
    • 存算分离(计算层 + 存储层)
    • 多租户
    • 地理复制
    • 分层存储(热数据SSD、冷数据S3)
    • 统一队列和流模型
  • 创新点:
    • BookKeeper存储(高可靠、高性能)
    • Schema Registry(消息格式管理)
    • Functions(内置流处理)
  • 对比Kafka:
    • 优势:多租户、分层存储、更灵活
    • 劣势:社区规模小、生态不如Kafka成熟

NATS(2016年):

  • 云原生消息系统
  • 特点:
    • 轻量级(Go语言实现)
    • 极简设计
    • 高性能(百万级/秒)
    • 支持Kubernetes
  • 适用:
    • 微服务通信
    • IoT场景
    • Serverless

云厂商托管服务:

  • AWS SQS/SNS(2006/2010)
  • Azure Service Bus(2012)
  • Google Cloud Pub/Sub(2015)
  • 阿里云MQ、腾讯云CMQ/TDMQ
  • 优势:
    • 免运维
    • 弹性伸缩
    • 按需付费
  • 劣势:
    • 厂商锁定
    • 成本可能较高(大规模)

1.4 目前在业界的地位和影响力

市场占有率与采用情况

主流消息队列对比:

产品发布年份语言吞吐量延迟适用场景市场份额
Kafka2011Scala/Java百万级/秒毫秒级大数据、日志、流处理60%
RabbitMQ2007Erlang万级/秒微秒级任务队列、微服务25%
RocketMQ2012Java十万级/秒毫秒级电商、金融10%
Pulsar2016Java百万级/秒毫秒级云原生、多租户3%
ActiveMQ2004Java万级/秒毫秒级传统企业2%

Kafka的主导地位:

  • LinkedIn、Netflix、Uber、Airbnb等头部公司使用
  • 大数据生态的标准组件(与Flink、Spark深度集成)
  • 日志收集的事实标准(替代Flume)
  • 实时数据管道的首选

RabbitMQ的稳定地位:

  • 开发者友好:学习曲线平缓
  • Python生态:Celery默认使用RabbitMQ
  • 中小型项目首选
  • 微服务架构中的常见选择

RocketMQ的特色定位:

  • 阿里巴巴背书:双11考验
  • 中国企业首选(特别是电商、金融)
  • 功能丰富:事务消息、顺序消息
  • 适合业务消息(而非大数据日志)

典型应用场景统计

场景分布:

  1. 异步解耦(40%):

    • 用户注册后的后续操作
    • 订单创建后的流程编排
    • 数据同步(如MySQL → ES)
  2. 流量削峰(25%):

    • 秒杀、抢购
    • 活动促销
    • 流量突发场景
  3. 日志收集(20%):

    • 应用日志汇总
    • 访问日志分析
    • 系统监控数据
  4. 消息通知(10%):

    • 邮件/短信发送
    • 推送通知
    • 告警通知
  5. 数据同步(5%):

    • 跨系统数据同步
    • 缓存更新
    • 搜索引擎索引

行业应用案例

电商行业:

  • 阿里巴巴:

    • RocketMQ处理双11交易链路
    • Kafka处理日志和数据流
    • 每天处理万亿级消息
  • 京东:

    • 自研消息队列JMQ
    • 基于Kafka改造
    • 支撑618大促

金融行业:

  • 银行核心系统:

    • 传统:IBM MQ
    • 现代化改造:RocketMQ(事务消息)
  • 证券交易:

    • 低延迟要求:Kafka(优化配置)
    • 可靠性要求:RocketMQ

互联网行业:

  • LinkedIn:

    • Kafka创始公司
    • 每天处理7万亿消息
    • 用于活动流、日志、指标
  • Netflix:

    • 全球最大视频流公司
    • Kafka处理实时事件
    • 每天500TB数据
  • Uber:

    • Kafka处理位置数据
    • 实时匹配乘客和司机
    • 每秒数百万事件

物联网(IoT):

  • 智能家居:

    • MQTT协议 + 消息队列
    • 设备状态上报
    • 指令下发
  • 车联网:

    • 车辆位置、状态数据
    • 实时数据流分析
    • 每辆车每秒数百条消息

1.5 未来发展趋势和方向

趋势1:云原生与Serverless

Serverless消息队列:

  • AWS SQS:完全托管,按需付费
  • Google Cloud Pub/Sub:自动扩缩容
  • Azure Service Bus:无需管理基础设施

Kubernetes原生支持:

  • Kafka on K8s:Strimzi Operator
  • RabbitMQ on K8s:RabbitMQ Cluster Operator
  • Pulsar on K8s:原生支持

未来方向:

  • 更轻量级:资源占用更少
  • 更易运维:自动化部署、监控、扩容
  • 更低成本:按使用量付费,无需预留资源

趋势2:流处理一体化

消息队列 + 流处理融合:

  • Kafka Streams:Kafka内置流处理
  • Pulsar Functions:Pulsar内置计算
  • Flink on Kafka:深度集成

实时数据管道:

数据源 → Kafka → 流处理(Flink) → 数据仓库/实时大屏

未来方向:

  • 统一批处理和流处理(Lambda架构 → Kappa架构)
  • 更低延迟(毫秒级 → 微秒级)
  • 更强大的流处理能力(SQL、CEP)

趋势3:智能化与自动化

自适应配置:

  • 自动调整分区数
  • 自动扩缩容
  • 自动故障恢复

AI辅助运维:

  • 异常检测:预测消息堆积
  • 性能优化:自动调整参数
  • 容量规划:智能推荐集群配置

自动化治理:

  • Schema演进自动管理
  • 消息格式验证
  • 数据质量监控

趋势4:多协议与互操作性

协议融合:

  • MQTT(物联网) + Kafka
  • AMQP + Kafka(Kafka支持AMQP协议)
  • HTTP/2 + gRPC

生态互通:

  • Kafka Connect:与各种系统集成
  • Change Data Capture (CDC):数据库变更捕获
  • Schema Registry:跨系统消息格式管理

趋势5:边缘计算与IoT

边缘消息队列:

  • 轻量级MQ运行在边缘设备
  • 断网情况下本地缓存
  • 网络恢复后同步到云端

5G与IoT:

  • 海量设备连接(百亿级)
  • 超低延迟(毫秒级)
  • 边云协同

挑战与机遇

技术挑战:

  • 性能:更高吞吐、更低延迟
  • 可靠性:99.999%可用性
  • 一致性:强一致性与性能的平衡
  • 复杂性:功能越来越多,学习成本增加

商业挑战:

  • 开源与云服务的竞争
  • 商业化路径(Confluent、StreamNative)
  • 成本优化

发展机遇:

  • 微服务架构普及:消息队列成为标配
  • 实时数据需求增长:从批处理到流处理
  • IoT爆发:海量设备数据传输
  • 边缘计算:新的应用场景

二、核心概念与设计理念

2.1 消息队列的核心设计理念

设计理念1:解耦

传统紧耦合的问题:

场景:订单支付成功后的处理

紧耦合代码:
class OrderService {
    void paymentSuccess(Order order) {
        // 更新订单状态
        updateOrderStatus(order.getId(), "PAID");

        // 发送邮件
        emailService.sendPaymentSuccessEmail(order);

        // 发送短信
        smsService.sendPaymentSuccessSMS(order);

        // 赠送积分
        pointsService.grantPoints(order.getUserId(), order.getAmount() * 0.01);

        // 更新库存
        inventoryService.decrementStock(order.getProductId(), order.getQuantity());

        // 生成发货单
        shippingService.createShippingOrder(order);

        // 同步到数据仓库
        dataWarehouseService.syncOrder(order);
    }
}

问题:
1. 修改困难:新增一个处理步骤,必须修改OrderService代码
2. 测试困难:测试OrderService需要所有依赖服务都可用
3. 性能问题:串行执行,总响应时间 = 各步骤时间之和
4. 可用性问题:任何一个服务故障,整个流程失败
5. 扩展性问题:无法针对单个步骤独立扩容

消息队列解耦方案:

订单服务:
class OrderService {
    void paymentSuccess(Order order) {
        // 1. 更新订单状态
        updateOrderStatus(order.getId(), "PAID");

        // 2. 发布事件
        messageQueue.publish("order.payment.success", order);

        // 完成!响应时间:几十毫秒
    }
}

邮件服务(订阅者):
messageQueue.subscribe("order.payment.success", (order) -> {
    emailService.sendPaymentSuccessEmail(order);
});

短信服务(订阅者):
messageQueue.subscribe("order.payment.success", (order) -> {
    smsService.sendPaymentSuccessSMS(order);
});

积分服务(订阅者):
messageQueue.subscribe("order.payment.success", (order) -> {
    pointsService.grantPoints(order.getUserId(), order.getAmount() * 0.01);
});

// ... 其他订阅者

优势:
1. 修改简单:新增处理步骤,只需新增订阅者,无需改OrderService
2. 测试简单:OrderService只需验证消息是否正确发送
3. 性能好:OrderService快速返回,后续异步处理
4. 高可用:某个订阅者故障,不影响订单支付流程
5. 易扩展:可以针对单个订阅者独立扩容

设计理念2:异步

同步调用的性能瓶颈:

场景:社交平台发帖

同步方式:
1. 用户发布帖子
2. 保存帖子内容(100ms)
3. 内容审核(500ms)
4. 生成缩略图(200ms)
5. 推送给粉丝(300ms)
6. 更新推荐算法(400ms)
7. 数据分析记录(100ms)

总耗时:1.6秒

用户体验:
- 点击发布后,等待1.6秒才看到"发布成功"
- 界面卡顿,用户焦虑
- 高并发时,响应时间更长

消息队列异步方案:

异步方式:
1. 用户发布帖子
2. 保存帖子内容(100ms)
3. 发送消息到MQ(5ms)
4. 立即返回"发布成功"

总耗时:105ms

后台异步处理(并发):
- 审核服务:收到消息,开始审核(500ms)
- 图片服务:收到消息,生成缩略图(200ms)
- 推送服务:收到消息,推送给粉丝(300ms)
- 推荐服务:收到消息,更新算法(400ms)
- 分析服务:收到消息,记录数据(100ms)

实际完成时间:约500ms(最慢的审核服务)

用户体验:
- 点击发布后,105ms看到"发布成功"
- 界面流畅,无等待感
- 后续处理在后台完成,用户无感知

异步带来的复杂性:

问题:
1. 一致性:消息发送成功,但处理失败怎么办?
2. 顺序性:消息乱序怎么办?
3. 重复消费:消息被处理多次怎么办?
4. 监控难度:如何知道异步任务的执行状态?

解决方案:
1. 一致性:本地消息表 + 定时任务重试
2. 顺序性:分区消费 + 顺序队列
3. 重复消费:幂等性设计(唯一ID、数据库唯一约束)
4. 监控:消息追踪、链路追踪(如Jaeger)

设计理念3:削峰填谷

流量峰值的应对策略:

场景:抽奖活动

流量特征:
- 活动开始前:10 QPS
- 活动开始瞬间:50,000 QPS(5000倍)
- 持续高峰:5分钟
- 回落至:100 QPS

传统架构(直接处理):
- 服务器配置:支持10 QPS
- 峰值到来:服务器CPU 100%,内存耗尽
- 大量请求超时,用户体验极差
- 服务崩溃,雪崩效应

扩容方案:
- 扩容到5000倍(500台服务器)
- 成本:假设每台1000元/月,500台 = 50万元/月
- 只用5分钟,剩余时间闲置
- 成本/收益比极低

限流方案:
- 限制QPS为10
- 99.98%的请求被拒绝
- 用户体验极差,大量投诉

消息队列削峰方案:

架构:
用户请求 → 快速写入MQ → 消费者恒定速率处理

峰值时:
1. 50,000请求/秒快速写入MQ(几乎无延迟)
2. 返回用户:"请求已提交,排队中..."
3. MQ中积压:50,000 × 300秒 = 1500万条消息

消费处理:
- 消费者数量:10个
- 每个消费者速率:10 QPS
- 总处理能力:100 QPS
- 处理完1500万消息需要:约42小时

优化后:
- 增加消费者到100个
- 总处理能力:1000 QPS
- 处理完需要:约4小时

用户体验:
- 立即得到反馈:"排队中,预计4小时后开奖"
- 4小时后收到通知:"您已中奖"或"很遗憾未中奖"
- 可接受的等待时间

成本优化:
- 无需扩容服务器
- MQ存储成本极低(相对于服务器)
- 100个消费者可以用现有服务器

设计理念4:可靠性

消息可靠性的三个阶段:

生产者 → Broker → 消费者

每个阶段都可能丢失消息:
1. 生产者 → Broker:
   - 网络故障,消息发送失败
   - Broker宕机,消息未持久化

2. Broker:
   - 磁盘损坏,消息丢失
   - Broker宕机且未复制

3. Broker → 消费者:
   - 消费者获取消息后崩溃,未处理
   - 消费者处理失败,未重试

生产者可靠性保证:

问题:如何确保消息发送成功?

方案1:同步确认
producer.send(message);
boolean success = producer.waitForAck();  // 等待Broker确认
if (!success) {
    // 重试或记录日志
}

优点:简单,可靠
缺点:性能差,每次发送都阻塞等待

方案2:异步确认
producer.send(message, new Callback() {
    void onSuccess() {
        // 发送成功
    }
    void onFailure(Exception e) {
        // 发送失败,重试
        retryQueue.add(message);
    }
});

优点:性能好,非阻塞
缺点:复杂度高,需要处理回调

方案3:本地消息表
@Transactional
void createOrder(Order order) {
    // 1. 保存订单
    orderRepository.save(order);

    // 2. 保存本地消息
    localMessageRepository.save(new Message("order.created", order));

    // 3. 提交事务
}

// 定时任务:扫描未发送的消息
@Scheduled(fixedDelay = 1000)
void sendPendingMessages() {
    List<Message> messages = localMessageRepository.findPending();
    for (Message msg : messages) {
        producer.send(msg);
        msg.setStatus("SENT");
    }
}

优点:
- 消息和业务操作在同一个事务
- 保证消息一定发送
- 可重试

缺点:
- 需要额外的消息表
- 定时任务有延迟

Broker可靠性保证:

问题:如何防止Broker丢失消息?

方案1:消息持久化
配置:
- 消息持久化到磁盘
- 刷盘策略:同步刷盘(安全)vs 异步刷盘(性能)

同步刷盘:
- 消息写入磁盘后,才返回ACK
- 优点:不丢消息
- 缺点:性能差(磁盘IO是瓶颈)

异步刷盘:
- 消息写入操作系统缓存,立即返回ACK
- 定期批量刷盘(如1秒一次)
- 优点:性能好
- 缺点:可能丢失最后1秒的消息

选择:
- 金融、支付:同步刷盘
- 日志、监控:异步刷盘

方案2:副本机制
配置:
- 消息复制到多个Broker
- 副本数:通常3副本

工作流程:
1. 生产者发送消息到Leader
2. Leader写入本地日志
3. Leader复制到所有Follower
4. 所有Follower确认后,Leader返回ACK

容错能力:
- 3副本可以容忍2个Broker故障
- 自动选举新Leader

权衡:
- 副本越多,越可靠,但性能越差
- 通常3副本足够

消费者可靠性保证:

问题:如何确保消息被正确处理?

错误方式:自动ACK
channel.basicConsume("queue", true, (msg) -> {
    processMessage(msg);  // 如果这里抛异常,消息已被ACK,丢失了!
});

正确方式:手动ACK
channel.basicConsume("queue", false, (msg) -> {
    try {
        processMessage(msg);
        channel.ack(msg);  // 处理成功后才ACK
    } catch (BusinessException e) {
        // 业务异常,拒绝消息,不重试
        channel.reject(msg, false);
    } catch (Exception e) {
        // 系统异常,拒绝消息并重新入队
        channel.reject(msg, true);
    }
});

重试策略:
1. 立即重试:可能立即再次失败
2. 延迟重试:等待一段时间后重试(指数退避)
   - 第1次:1秒后
   - 第2次:2秒后
   - 第3次:4秒后
   - ...
3. 死信队列:重试N次后,进入死信队列,人工处理

幂等性:
由于可能重试,消费者必须保证幂等性:
- 使用唯一ID去重(Redis SETNX)
- 数据库唯一约束
- 业务逻辑天然幂等(如SET操作)

2.2 消息队列的架构设计

核心组件1:Producer(生产者)

生产者的职责:

1. 消息创建:
   - 业务数据序列化(JSON、Protobuf等)
   - 添加元数据(时间戳、消息ID等)

2. 消息发送:
   - 选择目标(Topic、Exchange、Queue)
   - 指定路由规则(Routing Key、Partition Key)
   - 发送到Broker

3. 可靠性保证:
   - 等待确认(同步/异步)
   - 失败重试
   - 本地缓存(断网时)

4. 性能优化:
   - 批量发送
   - 压缩
   - 异步发送

生产者的关键配置:

// Kafka 生产者配置
Properties props = new Properties();

// 基础配置
props.put("bootstrap.servers", "localhost:9092");  // Broker地址
props.put("key.serializer", "org.apache.kafka.common.serialization.StringSerializer");
props.put("value.serializer", "org.apache.kafka.common.serialization.StringSerializer");

// 可靠性配置
props.put("acks", "all");  // 等待所有副本确认
// acks=0: 不等待,最快但可能丢消息
// acks=1: 等待Leader确认
// acks=all: 等待所有副本确认,最可靠但最慢

props.put("retries", 3);  // 失败重试次数
props.put("retry.backoff.ms", 100);  // 重试间隔

// 性能配置
props.put("batch.size", 16384);  // 批量大小16KB
props.put("linger.ms", 10);  // 等待10ms,凑够一批再发送
props.put("compression.type", "lz4");  // 压缩算法

// 幂等性配置
props.put("enable.idempotence", true);  // 启用幂等性,防止重复

KafkaProducer<String, String> producer = new KafkaProducer<>(props);

批量发送优化:

单条发送 vs 批量发送:

单条发送:
for (int i = 0; i < 10000; i++) {
    producer.send(new Message("hello" + i));
}
// 10000次网络往返
// 耗时:假设每次1ms,总共10秒

批量发送:
List<Message> batch = new ArrayList<>();
for (int i = 0; i < 10000; i++) {
    batch.add(new Message("hello" + i));
    if (batch.size() == 100) {
        producer.sendBatch(batch);  // 100条一批
        batch.clear();
    }
}
// 100次网络往返
// 耗时:100ms

性能提升:100倍!

注意:
- 批量大小:太小(如10条)提升有限,太大(如10000条)内存占用高
- 推荐:100-1000条/批,或1-16MB/批

核心组件2:Broker(消息服务器)

Broker的职责:

1. 消息存储:
   - 持久化到磁盘
   - 索引管理
   - 过期消息清理

2. 消息路由:
   - 根据规则将消息路由到队列
   - 支持多种路由策略(Direct、Topic、Fanout等)

3. 集群管理:
   - 节点间通信
   - Leader选举
   - 副本同步

4. 客户端管理:
   - 连接管理
   - 权限验证
   - 流量控制

存储架构:

Kafka的存储设计:

Topic
  └── Partition 0
        ├── 00000000000000000000.log (数据文件)
        ├── 00000000000000000000.index (索引文件)
        └── 00000000000000000000.timeindex (时间索引)
  └── Partition 1
        ├── 00000000000000000000.log
        ├── 00000000000000000000.index
        └── 00000000000000000000.timeindex
  └── Partition 2
        └── ...

特点:
1. 顺序写:消息追加到日志末尾,利用磁盘顺序写性能(400MB/s)
2. 分段存储:每个Partition拆分为多个Segment(1GB一段)
3. 索引加速:稀疏索引,快速定位消息位置
4. 零拷贝:sendfile系统调用,减少数据拷贝

副本机制:

Kafka副本架构:

Topic: orders, Partition 0, Replication=3

Broker 1 (Leader):
  - orders-0-leader (可读可写)

Broker 2 (Follower):
  - orders-0-follower-1 (只读,同步Leader数据)

Broker 3 (Follower):
  - orders-0-follower-2 (只读,同步Leader数据)

写入流程:
1. 生产者发送消息到Leader
2. Leader写入本地日志
3. Leader同步到Follower
4. 所有Follower确认后,Leader提交
5. Leader返回ACK给生产者

故障恢复:
- Broker 1(Leader)宕机
- Zookeeper检测到故障
- 从Follower中选举新Leader(Broker 2)
- 生产者和消费者自动切换到新Leader

ISR(In-Sync Replicas):
- 与Leader保持同步的副本集合
- 只有ISR中的副本才能被选为Leader
- 落后太多的副本会被移出ISR

核心组件3:Consumer(消费者)

消费者的职责:

1. 消息拉取:
   - 从Broker拉取消息(Pull模式)
   - 或Broker推送消息(Push模式)

2. 消息处理:
   - 反序列化
   - 业务逻辑处理

3. 位置管理:
   - 记录消费位置(Offset)
   - 提交Offset到Broker

4. 失败处理:
   - 重试
   - 死信队列

消费模型:

1. 点对点模型(Queue):
   生产者 → Queue → 消费者1
                 → 消费者2 (竞争消费)
                 → 消费者3

   特点:
   - 一条消息只被一个消费者消费
   - 消费后消息从队列删除
   - 多个消费者负载均衡

   适用:
   - 任务分发
   - 负载均衡

2. 发布订阅模型(Topic):
   生产者 → Topic → 订阅者1(独立消费)
                  → 订阅者2(独立消费)
                  → 订阅者3(独立消费)

   特点:
   - 一条消息被多个订阅者消费
   - 每个订阅者维护自己的消费位置
   - 消息不会被删除(直到过期)

   适用:
   - 事件广播
   - 日志收集
   - 数据同步

消费者组(Consumer Group):

Kafka的消费者组设计:

Topic: orders (3个Partition)

Consumer Group 1:
  - Consumer A: 消费 Partition 0
  - Consumer B: 消费 Partition 1
  - Consumer C: 消费 Partition 2

Consumer Group 2:
  - Consumer D: 消费 Partition 0, 1, 2

特点:
1. 同一个Consumer Group内:
   - 一个Partition只能被一个Consumer消费
   - 实现负载均衡

2. 不同Consumer Group间:
   - 独立消费,互不影响
   - 实现发布订阅

3. 分区分配策略:
   - Range:按Partition范围分配
   - RoundRobin:轮询分配
   - Sticky:尽量保持之前的分配,减少Rebalance

Rebalance(重平衡):
- 触发条件:Consumer加入/退出,Partition增加
- 过程:停止消费 → 重新分配Partition → 恢复消费
- 问题:Rebalance期间,整个Consumer Group停止消费
- 优化:减少Rebalance频率,缩短Rebalance时间

Offset管理:

Offset(偏移量):表示消费者在Partition中的消费位置

自动提交 vs 手动提交:

1. 自动提交:
props.put("enable.auto.commit", "true");
props.put("auto.commit.interval.ms", "5000");  // 每5秒自动提交

优点:简单,无需手动管理
缺点:可能丢消息(消息处理失败,但Offset已提交)

2. 手动提交:
props.put("enable.auto.commit", "false");

while (true) {
    ConsumerRecords<String, String> records = consumer.poll(Duration.ofMillis(100));
    for (ConsumerRecord<String, String> record : records) {
        try {
            processMessage(record.value());
            consumer.commitSync();  // 同步提交
            // 或 consumer.commitAsync(); // 异步提交
        } catch (Exception e) {
            // 处理失败,不提交Offset,下次重新消费
        }
    }
}

优点:精确控制,不丢消息
缺点:需要手动管理,复杂度高

最佳实践:
- 重要业务:手动提交
- 非关键日志:自动提交

核心组件4:Topic & Partition

Topic(主题):

定义:消息的分类,类似数据库的表

特点:
- 逻辑概念,实际数据存储在Partition中
- 支持多个生产者和消费者
- 可配置副本数、保留时间等

示例:
- orders: 订单相关消息
- user_events: 用户行为事件
- logs: 应用日志

Partition(分区):

定义:Topic的物理分片,实际存储单元

作用:
1. 水平扩展:
   - 单个Topic可以分布在多个Broker
   - 突破单机存储和性能限制

2. 并行处理:
   - 多个Partition可以并行读写
   - Consumer可以并行消费

3. 有序性:
   - 单个Partition内消息有序
   - 不同Partition间无序

分区策略:
1. 轮询(Round-Robin):
   - 消息依次发送到各个Partition
   - 负载均衡

2. 按Key分区:
   - 相同Key的消息发送到同一Partition
   - 保证相同Key的消息有序

   示例:
   // 用户ID作为Key,同一用户的消息在同一Partition,保证顺序
   producer.send(new ProducerRecord<>("user_events", userId, message));

3. 自定义分区器:
   class CustomPartitioner implements Partitioner {
       int partition(String topic, Object key, byte[] keyBytes,
                     Object value, byte[] valueBytes, Cluster cluster) {
           // 自定义分区逻辑
           if (key == null) return 0;
           return Math.abs(key.hashCode()) % cluster.partitionCountForTopic(topic);
       }
   }

分区数选择:
- 太少:无法充分利用集群资源
- 太多:管理开销大,Rebalance慢
- 推荐:节点数 × (1-3)
  例如:10个Broker,建议10-30个Partition

2.3 消息队列与同类技术的核心差异

消息队列 vs RPC(远程过程调用)

RPC的特点:

同步调用:
OrderService:
  Order order = createOrder();
  Payment payment = paymentService.createPayment(order);  // 同步等待
  Shipping shipping = shippingService.createShipping(order);  // 同步等待
  return order;

特点:
- 强耦合:调用方需要知道被调用方的接口
- 同步阻塞:等待远程服务返回
- 实时性:立即得到结果

适用场景:
- 需要立即得到结果:如查询余额、验证密码
- 强一致性要求:如转账
- 调用链路短:2-3层

消息队列的特点:

异步解耦:
OrderService:
  Order order = createOrder();
  messageQueue.send("order.created", order);  // 异步发送,立即返回
  return order;

PaymentService:
  messageQueue.subscribe("order.created", (order) -> {
      createPayment(order);
  });

ShippingService:
  messageQueue.subscribe("order.created", (order) -> {
      createShipping(order);
  });

特点:
- 松耦合:通过消息通信,服务间无直接依赖
- 异步非阻塞:发送后立即返回
- 最终一致性:不保证实时

适用场景:
- 不需要立即结果:如发送邮件、生成报表
- 解耦:服务间独立演进
- 流量削峰:缓冲突发流量

对比总结:

维度RPC消息队列
调用方式同步异步
耦合度强耦合松耦合
性能取决于被调用方快速返回
可用性被调用方故障则失败被调用方故障不影响发送方
一致性强一致性最终一致性
适用场景查询、验证、需要结果通知、异步处理、解耦

混合使用:

实际业务中,两者经常混合使用:

示例:电商下单
1. 同步RPC:
   - 检查库存(需要立即知道是否有货)
   - 创建订单(需要立即返回订单号)

2. 异步消息队列:
   - 发送订单确认邮件
   - 推送给物流系统
   - 更新用户积分
   - 数据分析

原则:
- 需要结果或强一致性:用RPC
- 不需要结果或可接受最终一致性:用消息队列

消息队列 vs 数据库

数据库存储消息的方案:

-- 消息表
CREATE TABLE messages (
    id BIGINT PRIMARY KEY,
    topic VARCHAR(100),
    content TEXT,
    status VARCHAR(20),  -- PENDING, PROCESSING, DONE
    created_at TIMESTAMP
);

-- 生产者:插入消息
INSERT INTO messages (topic, content, status) VALUES ('order.created', '...', 'PENDING');

-- 消费者:轮询查询
SELECT * FROM messages WHERE status = 'PENDING' LIMIT 100;

问题:
1. 性能差:
   - 每次轮询都要查询数据库
   - SELECT查询走索引,但仍比消息队列慢
   - 大量消息时,表会很大,查询慢

2. 并发问题:
   - 多个消费者可能取到同一条消息
   - 需要加锁或乐观锁,复杂且影响性能

3. 消息堆积:
   - 消息未及时消费,表越来越大
   - 需要定期清理已消费消息

4. 功能缺失:
   - 无顺序保证
   - 无重试机制
   - 无死信队列
   - 无流量控制

专业消息队列的优势:

1. 高性能:
   - 内存队列 + 磁盘持久化
   - 批量操作
   - 零拷贝技术
   - 性能:十万到百万QPS

2. 高可用:
   - 副本机制
   - 自动故障转移
   - 集群部署

3. 丰富功能:
   - 消息路由
   - 消息重试
   - 死信队列
   - 延迟消息
   - 优先级队列

4. 运维友好:
   - 监控界面
   - 性能指标
   - 告警机制

何时用数据库,何时用消息队列:

使用数据库的场景:
- 消息量小(如每秒几条)
- 需要复杂查询(如按时间范围、多条件过滤)
- 需要持久化很长时间(如几个月、几年)
- 已有数据库基础设施,不想引入新组件

使用消息队列的场景:
- 消息量大(如每秒成百上千)
- 不需要复杂查询
- 保留时间短(如几天、几周)
- 需要高性能、高可用
- 需要专业功能(如顺序、重试、延迟)

实际选择:
- 小项目、简单场景:数据库也能满足
- 中大型项目、高并发:必须用消息队列

消息队列 vs 事件总线(Event Bus)

事件总线(进程内):

// Guava EventBus示例
EventBus eventBus = new EventBus();

// 订阅
eventBus.register(new Object() {
    @Subscribe
    public void handleOrderCreated(OrderCreatedEvent event) {
        System.out.println("Order created: " + event.getOrderId());
    }
});

// 发布
eventBus.post(new OrderCreatedEvent(123));

特点:
- 进程内通信:只能在同一个JVM内
- 内存传递:极快,纳秒级
- 无持久化:进程重启,事件丢失
- 无分布式:无法跨服务

适用:
- 单体应用内的模块解耦
- 性能要求极高,可接受丢失

消息队列(分布式):

// Kafka示例
producer.send(new ProducerRecord<>("order.created", order));

consumer.subscribe(Arrays.asList("order.created"));
while (true) {
    ConsumerRecords<String, String> records = consumer.poll(Duration.ofMillis(100));
    for (ConsumerRecord<String, String> record : records) {
        System.out.println("Order created: " + record.value());
    }
}

特点:
- 分布式通信:跨进程、跨服务器
- 持久化:消息存储在磁盘
- 高可用:集群、副本
- 性能:毫秒级

适用:
- 微服务间通信
- 分布式系统
- 需要可靠性和持久化

对比:

维度事件总线消息队列
范围进程内跨进程、分布式
性能纳秒级毫秒级
可靠性不保证(内存)高可靠(持久化)
功能简单丰富(重试、死信等)
复杂度高(需要部署维护)
适用单体应用分布式系统

三、基础知识

3.1 RabbitMQ 安装部署

单机安装(开发环境)

Docker 安装(推荐):

# 1. 拉取镜像(带管理界面)
docker pull rabbitmq:3.12-management

# 2. 运行容器
docker run -d \
  --name rabbitmq \
  -p 5672:5672 \          # AMQP端口
  -p 15672:15672 \        # 管理界面端口
  -e RABBITMQ_DEFAULT_USER=admin \
  -e RABBITMQ_DEFAULT_PASS=admin123 \
  rabbitmq:3.12-management

# 3. 验证
# 访问管理界面:http://localhost:15672
# 用户名:admin,密码:admin123

# 4. 查看日志
docker logs -f rabbitmq

Linux 安装:

# Ubuntu/Debian
# 1. 添加仓库
echo "deb https://dl.cloudsmith.io/public/rabbitmq/rabbitmq-server/deb/ubuntu jammy main" | sudo tee /etc/apt/sources.list.d/rabbitmq.list

# 2. 导入GPG密钥
curl -1sLf 'https://dl.cloudsmith.io/public/rabbitmq/rabbitmq-server/gpg.E495BB49CC4BBE5B.key' | sudo apt-key add -

# 3. 安装
sudo apt update
sudo apt install rabbitmq-server

# 4. 启动
sudo systemctl start rabbitmq-server
sudo systemctl enable rabbitmq-server

# 5. 启用管理插件
sudo rabbitmq-plugins enable rabbitmq_management

# 6. 创建用户
sudo rabbitmqctl add_user admin admin123
sudo rabbitmqctl set_user_tags admin administrator
sudo rabbitmqctl set_permissions -p / admin ".*" ".*" ".*"

配置文件:

# /etc/rabbitmq/rabbitmq.conf

# 监听地址
listeners.tcp.default = 5672

# 管理插件
management.tcp.port = 15672

# 内存限制(40%物理内存)
vm_memory_high_watermark.relative = 0.4

# 磁盘限制(50GB)
disk_free_limit.absolute = 50GB

# 心跳间隔
heartbeat = 60

# 日志级别
log.console.level = info

集群部署(生产环境)

集群架构:

节点1(Disk): rabbitmq-1 (192.168.1.101)
节点2(Disk): rabbitmq-2 (192.168.1.102)
节点3(RAM):  rabbitmq-3 (192.168.1.103)

特点:
- Disk节点:元数据持久化到磁盘,重启不丢失
- RAM节点:元数据只在内存,性能更好但重启丢失
- 建议:至少2个Disk节点

集群搭建:

# 节点1
rabbitmqctl stop_app
rabbitmqctl reset
rabbitmqctl start_app

# 节点2加入集群
rabbitmqctl stop_app
rabbitmqctl reset
rabbitmqctl join_cluster rabbit@rabbitmq-1
rabbitmqctl start_app

# 节点3加入集群(RAM节点)
rabbitmqctl stop_app
rabbitmqctl reset
rabbitmqctl join_cluster --ram rabbit@rabbitmq-1
rabbitmqctl start_app

# 查看集群状态
rabbitmqctl cluster_status

# 输出:
{running_nodes,[rabbit@rabbitmq-3,rabbit@rabbitmq-2,rabbit@rabbitmq-1]},
{cluster_name,<<"rabbit@rabbitmq-1">>},
{partitions,[]},
{alarms,[{rabbit@rabbitmq-3,[]},{rabbit@rabbitmq-2,[]},{rabbit@rabbitmq-1,[]}]}

镜像队列(高可用):

# 设置所有队列镜像到所有节点
rabbitmqctl set_policy ha-all "^" '{"ha-mode":"all"}'

# 设置队列镜像到2个节点
rabbitmqctl set_policy ha-two "^" '{"ha-mode":"exactly","ha-params":2}'

# 设置特定队列镜像
rabbitmqctl set_policy ha-orders "^order\\." '{"ha-mode":"all"}'

3.2 基本配置和核心概念

Exchange(交换机)类型

1. Direct Exchange(直连交换机):

// 场景:日志分级处理

// 1. 声明交换机
channel.exchangeDeclare("logs_direct", BuiltinExchangeType.DIRECT, true);

// 2. 声明队列
channel.queueDeclare("error_queue", true, false, false, null);
channel.queueDeclare("info_queue", true, false, false, null);

// 3. 绑定(完全匹配routing key)
channel.queueBind("error_queue", "logs_direct", "error");
channel.queueBind("info_queue", "logs_direct", "info");

// 4. 发送消息
channel.basicPublish("logs_direct", "error", null, "数据库连接失败".getBytes());
// 只有error_queue会收到

channel.basicPublish("logs_direct", "info", null, "用户登录".getBytes());
// 只有info_queue会收到

适用场景:
- 日志分级
- 任务分发(按类型)
- 严格路由规则

2. Topic Exchange(主题交换机):

// 场景:订单事件处理

// 通配符:
// * : 匹配一个单词
// # : 匹配零个或多个单词

channel.exchangeDeclare("order_events", BuiltinExchangeType.TOPIC, true);

// 队列1:处理所有创建事件
channel.queueBind("create_queue", "order_events", "order.*.created");
// 匹配:order.payment.created, order.shipping.created
// 不匹配:order.created, order.payment.success

// 队列2:处理所有订单事件
channel.queueBind("all_order_queue", "order_events", "order.#");
// 匹配:order.created, order.payment.created, order.payment.success.confirmed

// 发送消息
channel.basicPublish("order_events", "order.payment.created", null, order.toBytes());
// create_queue 和 all_order_queue 都会收到

适用场景:
- 灵活的路由规则
- 多级分类(如:地区.城市.店铺)
- 订阅特定模式的事件

3. Fanout Exchange(扇出交换机):

// 场景:广播消息

channel.exchangeDeclare("notifications", BuiltinExchangeType.FANOUT, true);

// 多个队列绑定到同一个Exchange
channel.queueBind("email_queue", "notifications", "");    // routing key忽略
channel.queueBind("sms_queue", "notifications", "");
channel.queueBind("push_queue", "notifications", "");

// 发送消息
channel.basicPublish("notifications", "", null, "系统维护通知".getBytes());
// 所有队列(email, sms, push)都会收到

适用场景:
- 广播通知
- 缓存更新(广播到所有缓存节点)
- 实时数据同步

4. Headers Exchange(头部交换机):

// 场景:复杂路由规则

// 声明交换机
channel.exchangeDeclare("reports", BuiltinExchangeType.HEADERS, true);

// 绑定规则
Map<String, Object> headers = new HashMap<>();
headers.put("x-match", "all");  // all:所有属性都匹配, any:任一属性匹配
headers.put("format", "pdf");
headers.put("type", "report");
channel.queueBind("pdf_report_queue", "reports", "", headers);

// 发送消息
AMQP.BasicProperties props = new AMQP.BasicProperties.Builder()
    .headers(Map.of("format", "pdf", "type", "report"))
    .build();
channel.basicPublish("reports", "", props, reportData);

适用场景:
- 多维度路由
- 复杂匹配规则
- 不适合用routing key表达的场景

注意:性能不如Direct和Topic,少用

Queue(队列)属性

队列声明:

Map<String, Object> args = new HashMap<>();

// 1. 持久化
boolean durable = true;  // 队列持久化(Broker重启后仍存在)

// 2. 独占
boolean exclusive = false;  // false:可被多个连接使用, true:只能当前连接使用

// 3. 自动删除
boolean autoDelete = false;  // false:不自动删除, true:最后一个消费者断开后删除

// 4. 消息TTL(Time To Live)
args.put("x-message-ttl", 60000);  // 消息60秒后过期

// 5. 队列TTL
args.put("x-expires", 300000);  // 队列300秒无人使用后自动删除

// 6. 队列最大长度
args.put("x-max-length", 10000);  // 最多10000条消息

// 7. 队列最大大小
args.put("x-max-length-bytes", 104857600);  // 最大100MB

// 8. 溢出行为
args.put("x-overflow", "reject-publish");  // 超出后拒绝新消息
// 或 "drop-head" - 删除最旧的消息

// 9. 死信交换机
args.put("x-dead-letter-exchange", "dlx_exchange");
args.put("x-dead-letter-routing-key", "dlx");

// 10. 优先级
args.put("x-max-priority", 10);  // 支持0-10的优先级

channel.queueDeclare("my_queue", durable, exclusive, autoDelete, args);

Message(消息)属性

// 消息属性
AMQP.BasicProperties props = new AMQP.BasicProperties.Builder()
    // 1. 持久化
    .deliveryMode(2)  // 1:不持久化, 2:持久化到磁盘

    // 2. 优先级
    .priority(5)  // 0-10,需要队列支持优先级

    // 3. 消息ID
    .messageId(UUID.randomUUID().toString())

    // 4. 时间戳
    .timestamp(new Date())

    // 5. 过期时间
    .expiration("60000")  // 60秒后过期(字符串!)

    // 6. 内容类型
    .contentType("application/json")
    .contentEncoding("UTF-8")

    // 7. 关联ID(用于RPC)
    .correlationId("abc-123")

    // 8. 回复队列(用于RPC)
    .replyTo("rpc_reply_queue")

    // 9. 自定义头部
    .headers(Map.of(
        "source", "order-service",
        "retry", 0
    ))

    // 10. 应用ID
    .appId("my-application")

    .build();

channel.basicPublish("exchange", "routing.key", props, message.getBytes());

3.3 Java 客户端基础使用

生产者代码示例

import com.rabbitmq.client.*;

public class Producer {
    private static final String EXCHANGE_NAME = "orders";
    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("admin");
        factory.setPassword("admin123");
        factory.setVirtualHost("/");

        // 连接池配置
        factory.setAutomaticRecoveryEnabled(true);  // 自动重连
        factory.setNetworkRecoveryInterval(5000);   // 重连间隔5秒

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

            // 3. 声明交换机
            channel.exchangeDeclare(EXCHANGE_NAME, BuiltinExchangeType.DIRECT, true);

            // 4. 构造消息
            String message = "{\"orderId\":123,\"userId\":456,\"amount\":99.99}";

            AMQP.BasicProperties props = new AMQP.BasicProperties.Builder()
                .deliveryMode(2)  // 持久化
                .contentType("application/json")
                .messageId(UUID.randomUUID().toString())
                .timestamp(new Date())
                .build();

            // 5. 发送消息
            channel.basicPublish(EXCHANGE_NAME, ROUTING_KEY, props, message.getBytes("UTF-8"));

            System.out.println("消息发送成功: " + message);
        }
    }
}

消费者代码示例

import com.rabbitmq.client.*;

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");
        factory.setPort(5672);
        factory.setUsername("admin");
        factory.setPassword("admin123");

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

        // 1. 声明队列
        channel.queueDeclare(QUEUE_NAME, true, false, false, null);

        // 2. 设置QoS(限流)
        channel.basicQos(10);  // 每次最多10条未确认消息

        // 3. 创建消费者
        DeliverCallback deliverCallback = (consumerTag, delivery) -> {
            String message = new String(delivery.getBody(), "UTF-8");
            System.out.println("收到消息: " + message);

            try {
                // 处理业务逻辑
                processMessage(message);

                // 手动ACK
                channel.basicAck(delivery.getEnvelope().getDeliveryTag(), false);

            } catch (BusinessException e) {
                // 业务异常,拒绝消息,不重试
                System.err.println("业务处理失败: " + e.getMessage());
                channel.basicReject(delivery.getEnvelope().getDeliveryTag(), false);

            } catch (Exception e) {
                // 系统异常,拒绝消息并重新入队
                System.err.println("系统异常,重新入队: " + e.getMessage());
                channel.basicNack(delivery.getEnvelope().getDeliveryTag(), false, true);
            }
        };

        // 4. 开始消费(关闭自动ACK)
        channel.basicConsume(QUEUE_NAME, false, deliverCallback, consumerTag -> {});

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

    private static void processMessage(String message) throws Exception {
        // 业务处理逻辑
        Thread.sleep(100);  // 模拟处理耗时
    }
}

Spring Boot 集成

1. 添加依赖:

<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-amqp</artifactId>
</dependency>

2. 配置文件:

spring:
  rabbitmq:
    host: localhost
    port: 5672
    username: admin
    password: admin123
    virtual-host: /
    # 生产者确认
    publisher-confirm-type: correlated
    publisher-returns: true
    # 消费者配置
    listener:
      simple:
        acknowledge-mode: manual  # 手动ACK
        concurrency: 5            # 最小消费者数
        max-concurrency: 10       # 最大消费者数
        prefetch: 10              # 每次拉取10条

3. 配置类:

@Configuration
public class RabbitMQConfig {

    // 声明Exchange
    @Bean
    public DirectExchange orderExchange() {
        return new DirectExchange("orders", true, false);
    }

    // 声明Queue
    @Bean
    public Queue orderQueue() {
        Map<String, Object> args = new HashMap<>();
        args.put("x-message-ttl", 60000);  // 消息TTL
        args.put("x-max-length", 10000);   // 最大长度
        return new Queue("order_queue", true, false, false, args);
    }

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

4. 生产者:

@Service
public class OrderService {

    @Autowired
    private RabbitTemplate rabbitTemplate;

    public void createOrder(Order order) {
        // 业务逻辑
        orderRepository.save(order);

        // 发送消息
        rabbitTemplate.convertAndSend("orders", "order.created", order, message -> {
            MessageProperties props = message.getMessageProperties();
            props.setDeliveryMode(MessageDeliveryMode.PERSISTENT);
            props.setContentType("application/json");
            props.setMessageId(UUID.randomUUID().toString());
            return message;
        });
    }
}

5. 消费者:

@Component
public class OrderConsumer {

    @RabbitListener(queues = "order_queue")
    public void handleOrderCreated(@Payload Order order,
                                    @Header(AmqpHeaders.DELIVERY_TAG) long deliveryTag,
                                    Channel channel) throws IOException {
        try {
            // 处理业务逻辑
            System.out.println("处理订单: " + order.getOrderId());

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

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

3.6 "Hello World"示例

最简单的点对点消息

生产者:

public class HelloWorldProducer {
    public static void main(String[] args) throws Exception {
        // 1. 创建连接
        ConnectionFactory factory = new ConnectionFactory();
        factory.setHost("localhost");

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

        // 2. 声明队列
        channel.queueDeclare("hello", false, false, false, null);

        // 3. 发送消息
        String message = "Hello World!";
        channel.basicPublish("", "hello", null, message.getBytes());

        System.out.println("发送: " + message);

        // 4. 关闭连接
        channel.close();
        connection.close();
    }
}

消费者:

public class HelloWorldConsumer {
    public static void main(String[] args) throws Exception {
        // 1. 创建连接
        ConnectionFactory factory = new ConnectionFactory();
        factory.setHost("localhost");

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

        // 2. 声明队列(与生产者一致)
        channel.queueDeclare("hello", false, false, false, null);

        // 3. 创建消费回调
        DeliverCallback deliverCallback = (consumerTag, delivery) -> {
            String message = new String(delivery.getBody(), "UTF-8");
            System.out.println("收到: " + message);
        };

        // 4. 开始消费
        channel.basicConsume("hello", true, deliverCallback, consumerTag -> {});

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

通过以上基础知识的学习,你已经掌握了消息队列的核心概念和RabbitMQ的基本操作,可以开始构建简单的异步消息应用了!接下来我们将深入学习更高级的功能和实战场景。

四、核心功能详解

(由于篇幅限制,此处是文档的前三部分。完整文档将继续包含第四到第十三部分的详细内容,总计约2800-3000行,涵盖核心功能、应用场景、进阶技巧、高阶知识、生产环境实践、最佳实践、学习路线、学习资源、常见问题和实战练习等所有章节。)


此文档将继续完善后续章节...