消息队列核心知识|java知识点
消息队列的作用
消息队列在分布式系统中主要解决四个问题:
- 异步:将非核心操作异步处理,提升主流程响应速度。
- 削峰:高峰期将请求先写入队列,消费端按自身能力匀速消费,避免瞬时流量压垮下游服务。
- 解耦:生产者和消费者不需要感知彼此的存在,通过消息中间件间接通信,降低服务间的耦合度。
- 分布式事务:通过事务消息机制保证跨服务的数据最终一致性。
消息模型
点对点模型
一条消息只能被一个消费者消费。消费者从队列中取出消息后,该消息从队列中移除。
发布/订阅模型
一条消息可以被多个消费者消费,消费者之间互不影响。生产者将消息发送到 Topic,每个订阅该 Topic 的消费者组都能收到全量消息。
集群消费与广播消费
RocketMQ 在发布/订阅模型基础上扩展了两种消费模式:
- 集群消费:同一消费者组内的多个实例进行负载均衡,每条消息只被组内一个实例消费。
- 广播消费:同一消费者组内的每个实例都消费全部消息。
消费者组
消费者组(Consumer Group)解决大流量场景下的消费能力瓶颈问题:
- 队列分片:Topic 被拆分为多个队列(分区),每个队列分配给组内一个消费者。
- 重平衡(Rebalance):当消费者上下线时,自动重新分配队列与消费者的映射关系,保证每个队列有且只有一个消费者负责。
- 消费能力扩展:增加消费者实例即可水平扩展消费能力(受限于队列数量)。
顺序消息
由于多生产者并发发送、多队列写入、多消费者并发消费,MQ 天然无法保证全局消息顺序。RocketMQ 的顺序消息方案:
- 生产端:将需要保证顺序的消息(如同一订单的状态变更)通过相同的消息 Key 路由到同一个队列。
- 消费端:消费者对同一个队列进行单线程消费(加锁),保证同一队列内的消息按顺序处理。
- 局限性:顺序只在同一个队列内保证,多个队列之间不保证全局顺序。
消息幂等
为什么会出现重复消费
MQ 只能保证 At Least Once(至少消费一次),无法严格保证 Exactly Once。重复消费的原因:
- 生产者因网络超时重试发送。
- 消费者处理成功但提交 Offset 前崩溃,重平衡后消息被重新分配。
- 消费端异常触发重试机制。
幂等实现方式
- 数据库唯一索引:利用唯一约束防止重复插入。
- 去重表 / 业务唯一 ID:消费前先查询去重表是否已处理。
- Redis 前置过滤:在 Redis 中记录已消费的消息 ID,高并发场景下快速过滤重复消息。
- 高并发组合方案:Redis 做前置去重过滤,数据库做最终一致性兜底。
不丢消息
保证消息不丢失需要从三个环节入手:
生产端
- 使用同步发送(
send),确保消息成功到达 Broker 后再继续。 - 配置发送重试机制(默认重试 2 次),应对网络抖动。
Broker 端
- 同步刷盘:消息写入内存后立即刷到磁盘。安全性最高但性能较差。
- 异步刷盘:消息先写入内存,由后台线程定期刷盘。性能好但可能丢失少量消息。
- 主从复制:主节点收到消息后同步到从节点。建议至少 2 副本。
消费端
- 业务处理成功后再提交 Offset。
- 消费失败时抛出异常,触发 Broker 重试。
同步 vs 异步策略的选择
同步刷盘 + 同步复制虽然最安全,但会显著增加延迟,吞吐量大幅下降。大多数生产环境选择异步刷盘 + 异步复制,通过发送重试、主从复制和业务幂等来实现最终可靠性,在可靠性与性能之间取得工程上的平衡。
事务消息
RocketMQ 的事务消息解决了生产者本地事务与消息发送的一致性问题。
流程
- 发送半消息(Half Message):生产者先发送一条对消费者不可见的半消息到 Broker。
- 执行本地事务:半消息发送成功后,生产者执行本地数据库操作。
- 提交或回滚:根据本地事务的执行结果,向 Broker 发送 Commit(消息对消费者可见)或 Rollback(消息丢弃)。
- 回查机制:如果 Broker 长时间未收到 Commit/Rollback,会主动回查生产者本地事务状态,决定消息最终是提交还是回滚。
事务消息保证了消息发送与本地事务的原子性,最终实现跨服务的数据一致性。
RocketMQ 与 RabbitMQ 对比
| 特性 | RocketMQ | RabbitMQ |
|---|---|---|
| 开发语言 | Java | Erlang |
| 吞吐量 | 高(十万级/秒) | 中(万级/秒) |
| 延迟 | 毫秒级 | 微秒级 |
| 事务消息 | 原生支持 | 需要自行实现 |
| 顺序消息 | 支持 | 需要特殊配置 |
| 延迟消息 | 原生支持 | 通过 TTL + 死信队列实现 |
| 消息回溯 | 支持按时间回溯 | 不支持 |
| 生态 | 与 Java / Spring 生态深度集成 | 社区成熟,协议标准(AMQP) |
| 适用场景 | 大规模分布式系统、电商、金融 | 中小规模、需要灵活路由规则 |
简要总结:RabbitMQ 成熟稳重、生态完善,适合中小规模场景;RocketMQ 高吞吐、高可用,原生支持事务消息和延迟消息,更适合大规模分布式系统。
消费失败重试策略
当消费者处理消息失败时:
- 自动重试:RocketMQ 默认最多重试 16 次,重试间隔逐步增大(从 10 秒到 2 小时)。RabbitMQ 通过
nack+ 重回队列或死信队列实现重试。 - 死信队列(Dead Letter Queue):超过最大重试次数的消息进入死信队列,需要人工介入处理。
- 重试中的幂等保护:每次重试消费时都必须做幂等判断,防止重试导致重复处理。
本文内容整理自消息队列学习笔记,涵盖消息模型、消费机制、顺序消息、幂等、可靠投递、事务消息和 MQ 选型对比等核心知识点。
