消息队列 - 理论梳理

本文系统介绍消息队列概念,以及 RabbitMQ , RocketMQ , kafka 三个消息队列的核心概念。

为什么需要消息队列?

在单体项目中,如果没有消息队列,那么:
  1. 上游(用户)的操作(如生成视频)需要系统响应很久,那么上游就会长时间等待响应,不能做别的操作,阻塞线程。
  2. 大量用户使用生成视频功能,每个任务下游直接处理(因为中间没有任何缓冲),可能导致下游服务处理不过来崩溃。
  3. 系统可能在高峰时间段流量会突增,大部分时间处理的过来,如果盲目加机器,会造成成本效益低。

在分布式系统中,不同服务或模块之间需要通信。同步调用的缺点如下:

  1. 系统耦合度过高:服务间直接依赖,就像用胶水粘在一起。一个服务的变更或故障,可能直接影响到其他服务,维护和扩展变得困难。
  2. 同步阻塞导致性能瓶颈:主流程需要等待所有依赖操作完成才能继续。比如用户注册后,同步发送邮件、短信、初始化积分等,每一步都可能耗时,导致用户响应时间很长。
  3. 无法应对突发流量(峰值冲击) :在秒杀、大促等场景,瞬时流量远超系统处理能力。所有请求直接压到数据库等核心服务,极易导致系统崩溃。

消息队列(MQ) 的出现,就是为了解决这些痛点。它像是一个“中间人”或“缓冲带”,让服务间的通信更灵活、更可靠。

一般要用到消息队列多半是在分布式系统下。

消息队列

介绍

你可以把消息队列想象成一个**智能的邮局或消息中转站,临时(也可以持久)帮你存储待处理的任务。**
  • 消息:便是待执行的任务,比如生成视频任务太耗时了,可以先放进消息队列。
  • 生产者:产生任务的上游,可以是用户,上游服务等等,生产者是一个抽象的概念,任何往消息队列放任务的角色都是生产者。
  • 消费者:处理消息队列内任务的服务,同样是抽象概念。
  • 队列:负责存储任务的容器,可以是 redis数据库,也可以用集合来手搓一个容器,最常用的是 rabbitMQ, RocketMQ , kafka

消息队列有两个含义:

**指代整个技术/系统:**当我们说:“我们项目里用了消息队列”,这里指的是一整套异步通信的技术和解决方案。

指代具体的数据结构:当我们说:“消息被发送到消息队列里”,这里特指那个存储消息的 FIFO 数据结构,也就是队列

消息队列的作用

消息队列的核心价值主要体现在三个方面

解耦:生产者和消费者无需直接知道对方的存在,只约定好消息格式即可。新增或减少一个消费者,生产者完全不用改代码,就像我们不用知道快递小哥的存在,我们只要知道菜鸟驿站在哪就行。

异步:生产者发送消息后立即返回,不用等待消费者处理。非核心操作(如发送邮件、记录日志)异步处理,大大提升主流程响应速度和用户体验

削峰填谷:在流量高峰期,消息队列充当缓冲区,暂存大量请求。后端服务按照自己的处理能力平稳地从队列中消费消息,避免被瞬间洪流冲垮

**可靠性:**消息队列一般会有持久化消息和防止消息丢失的功能。

**顺序性:**保证消息按顺序消费

消息队列的模型

点对点(网上也说集群模式):一条消息只能有一个消费者消费,这条消息不能重复消费。

发布/订阅( 广播模型 ) : 消息队列会把一条消息广播给所有订阅了自己的消费者,都要老老实实处理。

Rabbit MQ 消息队列概念汇总

RabbitMQ 和 RocketMQ 关键概念区别:RabbitMQ没有 **NameServer ** **, **使用exchange + binding key + routing key 实现路由。 RabbitMQ 的集群信息和 Broker 数量由集群内部自己维护,不需要 NameServer 。

生产者(群组):生产消息给队列

消费者(群组):消费队列的消息

队列:存储消息的容器,Broker 管理的就是队列。

routing key : 路由键, 生产者在发送消息时指定的“路由标签” , Exchange 会根据这个 routing key 的匹配规则,将消息路由到符合条件的队列。

**binding key **: 绑定键, 队列在绑定到 Exchange 时所设置的匹配规则, Exchange 根据 routing key 与 binding key 是否匹配,决定是否将消息投递给该队列。

exchange : 交换机, 接收生产者发送的消息,根据消息携带的 routing key 与各个队列的 binding key 的匹配关系,将消息路由到一个或多个队列

Topic Exchange: 这是 rabbitMQ 对 topic 概念的具体实现, 允许 Binding Key 使用通配符(* 匹配一个词,# 匹配零个或多个词),实现灵活的模式匹配 , 落地实现主题的概念。

队列名字:队列的唯一标识,如果队列要持久化,那么队列名字是存储的路径组成。

Connection : TCP 连接,由客户端(Producer 或 Consumer)建立 ,一个 TCP 连接是单个通道,消息拆成多个包,那只能一个包一个包发送,而且只能先把这个消息的包发完,再发送下一个消息的包, 如果客户端多个线程同时往这个 TCP 发送消息,数据包会互相混在一起。如果开通多个Connection连接, 会多次 TCP 握手,消耗资源,于是 Channel 就来拯救这种情况。

Channel(通道):在一个连接里可以开多个轻量级通道,通道负责发送/接收消息。 这是RabbiMQ做的优化,一个逻辑概念,不是物理通道,给消息绑定一个 Channel_id 和channel会话实现,这样一个TCP连接 + 多个Channel 实现了多线程同时往 TCP 写消息。

channel会话:一个逻辑概念,本质上是一个数据结构实现,一个会话对象存储了 :

  • Channel 状态:open / close
  • 事务状态(transaction)
  • 消息确认状态(哪些消息已经 ack/nack)
  • QoS(prefetch):当前可以未 ack 的消息数
  • 绑定的队列、交换机信息

把会话对象和Channel_id 绑定在消息中,便实现了一个 TCP + 多个Channel , 这样多条消息的数据包可以混合、交替发送, 最大化利用单个 TCP 连接资源 ,榨干 TCP 。

RabbitMQ 属性(队列类型)
rabbitMQ 的队列可以设置属性,让队列具备某中特性,属性可以组合搭配:

**1)持久化队列(durable=true) : **Broker 重启后队列仍存在, Broker 在内存中维护队列对象,同时写入 磁盘文件。

  • 原理:消息设置 delivery_mode=2 ,便也支持持久化, Broker 收到消息后 , 将消息写入磁盘 , 仅当消息安全写入磁盘后才返回 ack 给 Producer 。
  • 消费者消费并确认(ack)消息 后,Broker 才会把消息从队列和磁盘上删除。

使用场景: 关键业务消息 (电商订单,物流消息推送) , 任务队列 (视频生成,图片处理等人物)都需要可靠性。

**2)独占队列(exclusive=true): ** 一种 只能被创建它的 Connection 使用的队列。

  • 原理:当客户端(Connection)声明一个 **独占队列, **Broker 会在内存中为这个队列分配一个逻辑对象,也就是connection对象的引用,以此来维护队列和连接的关联性。
    • 队列一旦创建,就和创建它的 Connection 绑定。
    • 当这个 Connection 关闭或断开时,队列会被自动删除(如果同时设置了 auto-delete)。
    • 只有创建该队列的 Connection 可以声明、消费消息。
    • 其他 Connection 无法访问(无法订阅或发送消息到这个队列)。

使用场景: 临时队列场景,在线客服系统 (每次打开客户咨询是新的对话), 多人协作软件、游戏实时状态更新

**3)自动删除队列(autoDelete=true): **当 最后一个消费者断开后,队列会被 Broker 自动删除。

  • 原理: Broker 为队列维护一个 **消费者计数,**当消费者全部断开, Broker 检查 autoDelete=true, 队列被自动删除

使用场景:和独占队列一样适用于临时队列,不同的是队列销毁方式。

**4)临时队列:**不指定队列名, Broker 自动生成一个唯一队列名 , 通常格式类似 amq.gen-<随机字符串>

  • 原理: 队列对象存储在 Broker 内存中,绑定以下属性:
    • exclusive=true → 队列绑定创建它的 Connection
    • autoDelete=true → Connection 关闭时自动删除

使用场景:这是 匿名临时独占队列 ,临时队列使用场景都相似,多一种技术选型。

消息特性
决定可靠性、消费顺序和优先级
概念定义作用 / 使用场景
持久化(Delivery Mode=2)消息写入磁盘,保证 Broker 重启后不会丢失关键业务消息,如订单、支付、任务队列
非持久化(Delivery Mode=1)消息只存在内存,Broker 重启会丢失临时通知、日志、实时数据
消息确认(Ack / Nack)消费者处理消息后向 Broker 确认,Broker 才删除消息确保消息被正确消费;支持 at-least-once 语义
预取 / QoS(prefetch)每个消费者一次可以接收的未 ack 消息数量控制消费速率,防止消费者处理不过来
TTL(Time-To-Live)消息过期时间,到期后自动删除临时消息、延迟消息
优先级消息消息带优先级,消费者先消费高优先级消息异步任务队列、紧急事件处理
死信(Dead Letter)无法正常消费的消息(拒绝、过期、队列满)被转入 DLX记录异常消息,做后续补偿或监控
交换机(Exchange)
决定消息路由策略
类型定义消息路由方式使用场景
Direct Exchange直连交换机根据 routing key
精确匹配队列
单点消息投递,例如任务队列
Fanout Exchange扇出交换机广播消息到绑定的所有队列广播通知、消息广播系统
Topic Exchange主题交换机支持通配符 *
#
匹配 routing key
日志系统、主题订阅
Headers Exchange头交换机根据消息头字段匹配队列灵活路由、条件匹配
默认交换机(Default / nameless)每个队列都绑定到默认交换机,routing key=队列名自动直连简单单队列发送
消费模式
决定消息接收和确认策略
模式定义特点 / 使用场景
Push 模式Broker 主动推送消息给消费者实时性好,常用模式
Pull 模式消费者主动拉取消息控制消费节奏,可结合批量拉取
自动 ack(auto-ack=true)消费者接收消息后自动确认简单快速,但存在消息丢失风险
手动 ack(manual ack)消费者处理完再发送 ack消费可靠性高,支持异常重试
事务模式(tx)发送或确认消息可回滚确保发送原子性,但性能低
Confirm 模式生产者确认消息是否被 Broker 收到高性能可靠发送,替代事务

如果把 RabbitMQ 的特性比作一个工具箱,事务就像是那个被放在角落里、布满灰尘、很少被想起来的旧工具。

RabbitMQ 的真正核心特性:

  1. 灵活的路由体系:这是 RabbitMQ 最核心、最强大的特性。通过 Direct、Topic、Fanout、Headers 四种 Exchange,可以实现极其复杂的消息路由逻辑,这是其他 MQ 难以比拟的。
  2. 高可靠性模型:通过消息持久化手动 ACK发布者确认,构建了一套完整、高效、可靠的端到端保障体系。
  3. 协议的标准化与扩展性:基于 AMQP 协议,使其天生具有跨语言、跨平台的互操作性。
  4. 应用解耦:这是所有消息队列的共性,但 RabbitMQ 通过其灵活的路由,将解耦能力发挥到了极致
高级特性
支撑业务扩展和可靠性保障
特性定义使用场景
死信队列(DLQ)消息因拒绝、过期或队列满无法消费,进入指定死信队列异常消息处理、补偿机制
TTL(消息/队列过期)消息或队列超过指定时间自动删除延迟消息、临时消息
优先级队列队列支持消息优先级,高优先级消息先消费紧急任务处理、异步调度
镜像队列(HA Queue)队列在集群节点间复制多份,保证高可用集群高可用、故障恢复
延迟队列 / 延时消息消息延迟指定时间后再投递定时任务、定时提醒
批量确认 / 批量发送对消息进行批量 ack 或批量发布提高吞吐量,减少网络开销
生产者工作原理
生产者的核心任务是:**将消息安全、高效地发送到指定的 Exchange**。它不关心消息的去向,只关心发送动作本身。
  1. 建立连接与通道
    • 生产者首先与 RabbitMQ Broker 建立一个 TCP 连接
    • 在这个连接之上,它会创建一个或多个通道。通道是轻量级的虚拟连接,所有的操作都在通道中进行,避免了为每个操作都建立 TCP 连接的开销。
  2. 开启可靠性模式(关键)
    • 为了确保消息不丢失,生产者需要开启可靠性机制。有两种选择:
      • 事务模式:通过 **channel.txSelect()** 开启。发送一批消息后,调用 **channel.txCommit()** 提交或 **channel.txRollback()** 回滚。此模式性能极差,已不推荐使用
      • 发布者确认模式:通过 **channel.confirmSelect()** 开启。这是异步的高性能模式。Broker 在成功接收消息后,会异步地向生产者发送一个确认(ACK)。
  3. 发布消息
    • 生产者调用 **channel.basicPublish()** 方法发送消息。
    • 此方法需要指定核心参数:Exchange 名称Routing Key(路由键)和消息体(包含消息内容和各种属性)。
  4. 接收确认
    • 在发布者确认模式下,生产者无需阻塞等待。它可以通过监听器异步接收 Broker 返回的 **basic.ack**
    • 如果收到 ACK,表示消息已成功到达 Broker。如果未收到(或收到 **nack**),生产者可以选择重发。

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

Exchange 工作原理
Exchange 是 RabbitMQ 的**消息路由核心**。它接收来自生产者的消息,并根据**路由规则**将消息投递到一个或多个队列中。它本身不存储消息。
  1. 接收消息:Exchange 接收生产者发来的消息,并解析出其中的 Routing Key
  2. 匹配绑定:Exchange 会查看与自己绑定的所有队列,以及每个队列的 Binding Key(绑定键)。
  3. 执行路由:根据自身的类型路由算法,决定将消息投递到哪些队列:
    • Direct Exchange:将 Routing Key 与 Binding Key 进行精确匹配。完全匹配则投递。
    • Fanout Exchange:忽略 Routing Key,将消息广播到所有绑定的队列。
    • Topic Exchange:将 Routing Key 与 Binding Key 进行模式匹配。支持 *****(匹配一个单词)和 **#**(匹配零个或多个单词)通配符。这是最灵活的类型。
    • Headers Exchange:不依赖 Routing Key,而是根据消息的 headers 属性进行匹配。
  4. 投递消息:将消息的副本发送到所有匹配的队列中。如果没有任何队列匹配,消息的行为取决于 Exchange 的配置(可以丢弃或返回给生产者)。

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

消费者工作原理
消费者的核心任务是:**从指定的队列中获取消息,并可靠地处理它**。
  1. 建立连接与通道:与生产者相同,消费者也需要建立连接(Connection)和通道(Channel)。
  2. 声明队列与绑定
    • 为了健壮性,消费者通常会尝试声明它要消费的队列(如果队列不存在则创建)。
    • 如果队列还未绑定到 Exchange,消费者也会执行绑定操作。
  3. 订阅消息
    • 消费者通过 **channel.basicConsume()** 方法向 Broker 订阅一个队列。
    • 这相当于告诉 Broker:“请持续将这个队列里的消息推送给我。” 这就是推模式,也是最常用的模式。
  4. 接收并处理消息
    • 当队列中有新消息时,Broker 会通过通道主动推送给消费者。
    • 消费者的回调函数被触发,接收到消息并开始执行业务逻辑。
  5. 发送确认
    • 消息处理完成后,消费者必须向 Broker 发送一个确认
    • 手动 ACK:通过 **channel.basicAck()** 显式确认。这是推荐做法,可以确保消息在处理失败时不会被丢失(可以重新入队或进入死信队列)。
    • 自动 ACK:消息一发送给消费者就自动确认。性能好,但容易在消费者处理失败时丢失消息。

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

死信队列工作原理
死信队列是 RabbitMQ **容错和监控**机制的核心组成部分。

死信队列本质上是一个普通的队列,从逻辑上它被用来存储“死掉”的消息。这里的“死”不是指消息本身损坏,而是指消息因为某些原因无法被正常消费

一条消息在以下三种情况下会成为“死信”:

  1. 消息被拒绝:消费者使用 **basicNack****basicReject**,并且设置了 **requeue=false**(不重新入队)。
  2. 消息过期:消息在队列中存活时间超过了设置的 TTL(下面会讲)。
  3. 队列已满:队列达到了设置的最大长度,无法再存入新消息。

死信队列本身就是一个普通队列,它的特殊之处在于它是通过死信交换机 配置而来的。

死信交换机(DLX, Dead Letter Exchange) 是一种特殊的交换机,用来接收那些在正常队列中无法被消费的“异常消息”。

当消息在一个队列中变成了“死信”,RabbitMQ 会自动把这条消息转发到对应的 死信交换机,再由 DLX 路由到一个新的队列(称为 死信队列 Dead Letter Queue, DLQ

在RabbitMQ中落地实现很简单,其实就是普通交换机什么也不用改,交换机名字用dlx.xx开头比较合理,绑定的队列也用dlx.xx开头,特殊之处在于正常队列配置一个map参数,指定死信交换机,指定死信路由键就行。

死信交换机:

channel.exchangeDeclare("dlx.exchange", "direct");
channel.queueDeclare("dlx.queue", true, false, false, null);
channel.queueBind("dlx.queue", "dlx.exchange", "dlx.key");

正常交换机指定死信交换机:

Map<String, Object> args = new HashMap<>();
args.put("x-dead-letter-exchange", "dlx.exchange");  // 指定死信交换机
args.put("x-dead-letter-routing-key", "dlx.key");   // 指定死信路由键(可选)

channel.exchangeDeclare("normal.exchange", "direct");
// 最后一个参数不是null就代表有死信队列
channel.queueDeclare("normal.queue", true, false, false, args); 
channel.queueBind("normal.queue", "normal.exchange", "normal.key");

工作原理图:

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

TTL(TIME-To-Live) **生存时间**
TTL,即**生存时间**。它可以为消息或队列设置一个过期时间,超过这个时间后,消息就会“死亡”。T

TL 是 RabbitMQ 消息生命周期管理的基础功能。

TTL 的两种设置方式

  1. 队列 TTL
    • 在创建队列时,设置 **x-message-ttl** 参数(单位:毫秒)。
    • 效果:所有进入该队列的消息都会继承这个过期时间。
    • 特点:一旦设置,队列中所有消息的过期时间都一样,无法为单条消息定制。
  2. 消息 TTL
    • 在发送每条消息时,设置 **expiration** 属性(单位:毫秒)。
    • 效果:只有这条消息拥有独立的过期时间。
    • 特点:可以为每条消息设置不同的过期时间,更加灵活。

TTL 的一个重要“坑”

消息过期后,并不会立即从队列中删除!

RabbitMQ 只有在两种情况下才会处理过期消息:

  1. 消息即将被消费者消费时(到达队列头部),没到达队列头部那就不会清理,哪怕过期了也是存在。
  2. RabbitMQ 的一个惰性扫描线程定期检查队列。

这意味着,如果队列积压严重,一条已经过期的消息可能还会在队列中存活一段时间,直到它被扫描到或到达队首。如果配置了 DLX,过期消息在被处理时就会变成死信。

工作原理图:

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

优先队列工作原理
一个支持消息优先级的队列。当队列中有消息积压时,高优先级的消息会**优先于**低优先级的消息被消费者获取。

优先级队列让 RabbitMQ 具备了按重要性处理消息的能力。

工作原理:

  1. 启用优先级:在创建队列时,必须设置 **x-max-priority** 参数(这就成为优先队列了),定义该队列支持的最大优先级(例如 5,表示优先级范围是 0-5)。
  2. 设置消息优先级:发送消息时,在消息属性中设置 **priority** 字段(值必须在队列支持的最大优先级范围内)。
  3. 内部排序:当消息进入队列时,RabbitMQ 并不是简单地追加到队尾。它会将高优先级的消息插入到队列中较低优先级消息的前面
  4. 消费顺序:消费者总是从队列头部获取消息,因此高优先级的消息会先被消费。

优先级队列结构图:

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

镜像队列工作原理
镜像队列:一个普通的队列,其内容可以被**实时复制**到一个或多个其他 Broker 节点上。形成“一主多从”的架构。
  • 主节点:负责处理所有对队列的读写操作(生产者发送、消费者消费)。
  • 从节点:作为热备,会从主节点同步所有消息和状态,但不对外提供服务。

镜像队列是 RabbitMQ 传统的高可用(HA)解决方案,用于保证在主节点故障时,队列中的消息不丢失,服务不中断。

⚠️已过时:在 RabbitMQ 3.8+ 版本后,官方推荐使用功能更强大、设计更现代的仲裁队列来替代镜像队列。

工作原理:

  1. 配置镜像策略:管理员需要在 RabbitMQ 管理界面或通过命令行设置一个镜像策略。这个策略定义了哪些队列需要被镜像,以及镜像到哪些节点上。
  2. 主从同步:当一个队列被策略匹配后,它就成为镜像队列。
    • 所有生产者发送的消息,都会先写入主节点。
    • 主节点会将消息同步给所有从节点。
    • 只有当所有从节点都确认收到后,主节点才会向生产者发送确认(ACK)。这保证了数据的强一致性。
  3. 故障转移
    • 如果主节点宕机,RabbitMQ 集群会自动从从节点中选举一个新的主节点
    • 原来的从节点升级为主节点,开始对外提供服务。
    • 这个过程对生产者和消费者是透明的,它们会自动重连到新的主节点。
仲裁队列工作原理
仲裁(Quorum)队列是 RabbitMQ **3.8 版本后推出的新一代高可用队列类型**。它基于 **Raft 共识算法**实现,旨在提供一个**数据安全、强一致、配置简单**的队列解决方案,是官方推荐的镜像队列替代品。

目标是实现高可用的消息队列架构。

“Quorum”一词意为“法定人数”,这暗示了它的核心机制:需要大多数节点同意,操作才能成功。

工作原理:基于 Raft 共识算法

仲裁队列的核心是 Raft 算法在消息队列场景下的实现。我们可以把它想象成一个民主委员会来管理队列。

一个仲裁队列通常部署在奇数个 Broker 节点上(最少 3 个),每个节点上的队列副本扮演一个角色:

  • Leader(领导者)唯一的领导者,负责处理所有来自生产者和消费者的请求(读写操作)。

  • Follower(跟随者):普通的委员,不对外提供服务。它们的主要工作是复制 Leader 的所有操作,并投票。

  • Learner(学习者):观察员角色,只同步日志,不参与投票。用于在不影响性能的情况下增加副本数,用于灾备或读取扩展。

  • 消息写入流程(核心)

这是理解仲裁队列的关键,它保证了数据的强一致性。

  1. 所有写操作必须通过 Leader
  2. 消息不是写入就成功,而是需要超过半数的节点(包括 Leader 自己)都确认写入日志后,才算真正“提交”。
  3. 只有已提交的消息才能被消费者消费。这确保了即使 Leader 宕机,新选举出的 Leader 也一定拥有所有已提交的消息,数据零丢失

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

  • 故障转移流程

心跳检测:Leader 会定期向所有 Follower 发送心跳。

触发选举:如果 Follower 在一段时间内没有收到 Leader 的心跳,它就认为 Leader 宕机,于是发起选举,将自己转为 Candidate(候选人)。

投票选举:Candidate 向其他节点请求投票。获得超过半数选票的节点成为新的 Leader。

数据恢复:新 Leader 上拥有所有已提交的消息,可以立即对外提供服务,整个过程自动完成,对客户端透明。

延迟队列工作原理
延迟队列(延迟消息):一个队列,其中的消息不会立即被消费者消费,而是在等待一段指定的时间后,才变成可消费状态。

延迟队列是一个非常常见的需求,但RabbitMQ 本身不直接提供延迟队列功能。我们需要通过插件或巧妙的组合来实现。

实现方式一:TTL + DLQ(经典组合)

这是最常用、最巧妙的实现方式,不依赖任何插件。

  1. 架构设计
    • 死信交换机:创建一个业务交换机业务队列(不设置 TTL)。
    • 延迟交换机:创建一个延迟交换机延迟队列(设置 TTL)。
    • 将延迟队列绑定到延迟交换机,并设置死信交换机为业务交换机。
  2. 工作流程
    1. 生产者发送消息到延迟交换机,并设置 TTL。
    2. 延迟交换机将消息路由到延迟队列
    3. 消息在延迟队列中等待,直到过期,期间不会有任何消费者拉取。
    4. 消息过期后,成为死信,被发送到死信交换机(即业务交换机)。
    5. 业务交换机根据路由规则,将消息路由到业务队列
    6. 消费者从业务队列中消费消息。

实现方式二:延迟插件

RabbitMQ 官方提供了一个 **rabbitmq_delayed_message_exchange** 插件,提供了更原生、更优雅的实现。

  1. 安装插件:在所有 RabbitMQ 节点上安装并启用该插件。
  2. 使用:插件会提供一个新的交换机类型 **x-delayed-message**
  3. 工作流程
    1. 生产者发送消息到 **x-delayed-message** 类型的交换机。
    2. 在消息头中添加 **x-delay** 属性,指定延迟时间(毫秒)。
    3. 交换机收到消息后,不会立即路由,而是将其保存在一个内部的 Mnesia 表中。
    4. 插件的后台定时器会定期检查这些消息。
    5. 当消息的延迟时间到达后,交换机才会像普通交换机一样,将消息路由到目标队列。

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

批量发送/批量确认工作原理
**批量发送**:**生产者**不是一次发送一条消息,而是将多条消息打包,一次性通过一个网络请求发送给 Broker。

批量确认消费者不是处理完一条消息就发送一次 ACK,而是处理完一批消息后,发送一个 ACK,确认这批消息都已成功处理。

这是 RabbitMQ 性能优化的关键手段,通过减少网络往返次数来提升吞吐量。

批量发送原理

  • 客户端实现:批量发送主要是客户端的行为。RabbitMQ 的 Java 客户端(如 Spring AMQP)提供了 **RabbitTemplate.convertAndSend** 的批量版本。
  • 网络效率:将 N 条消息的 N 次网络请求,合并为 1 次网络请求,极大地减少了网络 RTT(往返时间)的开销。
  • 权衡:批量发送会增加客户端的内存使用,并且会带来一定的延迟(需要等一批消息凑齐或超时),空间换时间。

批量确认原理

  1. 开启手动确认:消费者必须关闭自动 ACK (**autoAck=false**)。
  2. 处理消息:消费者从队列中拉取一批消息(或 Broker 推送一批),并依次处理。
  3. 发送批量 ACK:当一批消息都处理成功后,消费者调用 **channel.basicAck(deliveryTag, multiple=true)**
    • **deliveryTag**:这批消息中最后一条消息的标签。
    • **multiple=true**:这是关键!它告诉 Broker:“请确认并删除小于等于这个 **deliveryTag** 的所有消息”。
  4. Broker 处理:Broker 收到这个批量 ACK 后,会一次性将队列中这批已确认的消息全部清除。

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

RocketMQ 消息队列概念汇总

RocketMQ 非常适合应用在微服务架构中,经常作为微服务的消息中间件,所以下面的概念以微服务视角去思考、代入才会更好理解。

我们来看下面这张图,它囊括了⁢ RocketMQ 相关的组件和角色‏。nQUn/A40eCdWYf9kto3iV3xvomJm6VhDorI+PWjaSXk=

核心概念一览表

概念定义对应 RabbitMQ 类比说明 / 使用场景
生成者和消费者
Producer消息生产者RabbitMQ Producer发送消息到 Topic / Queue
Producer Group消息生产者群组事务,管理多个生产者
Consumer消息消费者RabbitMQ Consumer拉取或订阅消息
Consumer Group消费者组RabbitMQ Queue + 多消费者一组消费者共享 Topic 消息,实现负载均衡或广播
中间队列层概念
Broker消息服务器RabbitMQ Broker消息存储和分发节点
NameServer元数据服务RabbitMQ 没有对应(Exchange/Binding+Cluster管理)集群路由和 Broker 元数据注册中心
Topic消息主题RabbitMQ Exchange(逻辑路由)消息的逻辑分类,Producer 发送消息到 Topic
Message Queue (队列)物理队列RabbitMQ Queue存储消息的物理队列,由 Broker 管理
commitLogRocketMQ 核心物理文件/存储所有消息的实际内容
ConsumeQueue队列的索引文件/一条记录映射一条commitLog 记录
Message Queue Offset队列偏移量RabbitMQ 没有明确概念消费进度记录在 Broker 或 Consumer
消息层概念
Message消息对象RabbitMQ Message包含 Body、Topic、Tag、Keys 等
Tag消息标签RabbitMQ RoutingKey消费端可做消息过滤
Message Key消息唯一标识RabbitMQ MessageId / header用于消息追踪和查找
顺序消息消息保证严格顺序消费RabbitMQ 默认 FIFO(Queue 内)消息队列分区内顺序处理
RocketMQ 事务消息Producer 发送事务消息,最终提交或回滚RabbitMQ Transaction 或 Confirm 模式保证分布式事务可靠性
延迟消息 / Scheduled Message消息延迟投递RabbitMQ TTL + 延迟插件定时任务、延迟任务
死信消息(DLQ)消费失败或超过重试次数的消息RabbitMQ DLX异常消息处理

概念很多,最核心的是 commitLog 、Consume Queue 、 message key 、事务消息

生产者和消费者
**1) Producer** : **消息的发送方**,负责把消息发送到 **Topic(逻辑分类), **消息通过 Broker 进行存储,最终写入 CommitLog, Producer 可以选择不同的发送模式(同步、异步、单向)
  • 同步发送(sync):等待 Broker ACK,保证消息可靠性
  • 异步发送(async):回调方式,适合高吞吐量场景
  • 单向发送(one-way):不等待 ACK,适合日志或监控消息
    可以指定 Topic 和 Tag | 用于消息分类和过滤 |
    | 可设置 Message Key | 业务唯一标识,便于查询或幂等处理 |
    | 事务消息支持 | 可以发送事务消息,配合 Broker 做事务半消息/回查机制 |

生产者实例名字: RocketMQ 内部会为每个 Producer 实例分配唯一 ID,一般不需要自己命名。

  • 主要用于:区分组内不同实例Broker, 事务回查时定位,日志/监控
  • 每个 Producer 实例 在创建时必须 绑定一个 Producer Group 名称
  • Producer Group 的名字就是由 Producer 逻辑形成

2) Producer Group(生产者群组)一组逻辑上属于同一业务的生产者集合, 生产者群组主要服务于事务消息和消息路由

事务消息(Transactional Message)管理

  • RocketMQ 支持事务消息,需要事务回查—— Broker 不确定半消息状态时,会主动去询问生产者“这个事务到底成功还是失败?”
  • 事务消息处理流程:
    1. Producer 发送半消息(half message)到 Broker
    2. Broker 保存半消息,但不立即投递给消费者
    3. Broker 回查 Producer 是否提交事务
    4. Producer 根据事务状态提交或回滚半消息
  • Producer Group 在事务消息中非常关键:
    • Broker 根据 Producer Group 找到对应的生产者实例执行回查
    • Producer Group 起到 逻辑标识和回查路由作用

管理多实例生产者

  • 同一业务可能有多个生产者实例
  • 通过 Producer Group,可以把它们归到同一逻辑组
  • 便于监控、配置和事务管理

3) 消费者: 消息队列的消息,在广播模式下 ,消费者保存 Message Queue Offset , 表示消费进度,所以支持并行消费,多个 MessageQueue 可以并行拉取,实现高吞吐量。

  • 可选定义名字 , 如果不指定,RocketMQ 会生成一个 **唯一实例 ID , **名字用户故障恢复,日志/监控
  • 不指定名字 → Broker 会根据客户端 IP + PID 自动生成实例 ID
  • Broker 通过 Consumer Group + Instance Name 唯一定位一个消费实例

4) Consumer Group(消费者组) 是 一组逻辑上属于同一应用的消费者集合, 每个组有一个逻辑名称,用于 Broker 识别和管理

  • 只需给消费者启动时指定一个 Consumer Group 名称, 消费组就形成了,每个消费者只能指定一个名称。
  • 消费同一 Topic 时,组内消费者共享消息分配
  • 在 集群模式下,保证 每条消息只被组内一个消费者消费一次
  • 在 广播模式下,组内每个消费者都能消费所有消息

中间层队列概念
**1)Broker: ** 消息服务器,负责存储消息、管理队列、投递消息给消费者 , Broker 就是服务器实例。

**2)NameServer ** : 元数据注册中心,管理 Broker 地址和 Topic 路由信息 , 可以由轻量级服务器作为技术实现, 每个 Broker 启动时注册到 NameServer 。

3)Topic (主题): 消息主题,理解成分类或命名空间,必须要在Broker下声明 Topic , Topic 下是具体的 Message Queue , topic 的信息会上报到 NameServer, 所以**Topic 是消息的第一级路由标识,是最常用的路由单位。**消息最终会按照负载均衡的规则, 存储在归属于 topic 的 message Queues

4)message queue : 存储消息的队列容器,消息在队列内有顺序编号(MessageQueue Offset),用于顺序消费 ,负责对topic

c5)ommitLog : 是 RocketMQ 的 核心物理存储文件, 存储的就是 **消息的物理内容, **它按顺序 顺序追加(append) 所有消息,无论消息属于哪个 Topic 或 MessageQueue, 所以整个RocketMQ 仅有一个 commitLog . commitlog = 消息内容 + 消息属性 + 系统元数据 + 长度信息


**6)Consume Queue **: 队列的元信息 , ConsumeQueue 是索引文件 ,每条记录对应 MessageQueue 的一个 Offset,记录了该逻辑消息在 CommitLog 中的物理偏移,Consumer 通过它快速找到消息 ,内部结构 :

 ConsumeQueue[0] = <CommitLog offset, size=128, tag>
 ConsumeQueue[1] = <CommitLog offset, size=128, tag>
 ConsumeQueue[2] = <CommitLog offset, size=128, tag>
 这里的[0][1][2] 便是 Message Queue Offset

7)Message Queue Offset : 每一个消息中都有一个顺序编号,称为Offset , 这是 物理队列内消息的唯一序号,编号从0单调递增,假如队列内3条消息消费完了,再来一条新消息,从4开始编号, 不会重用 ,永远递增。

  • RocketMQ 崩溃后如何恢复编号? 通过 commitLog + ConsumeQueue
  • 重启后, Broker 会 读取 ConsumeQueue 文件尾部, 找到最后一条记录 <CommitLog offset, 消息长度, Tag>, 逻辑序号 = 该记录在文件中的顺序位置 , 即使消息已经被 Consumer 消费,ConsumeQueue 索引仍存在,所以可以恢复最大 Offset .
  • **集群模式下,由 Broker 集中管理****Message Queue Offset,**确保一致性和可靠性。
  • **广播模式下,由消费者各自本地管理****Message Queue Offset,**简单高效。

Rocket MQ 的 commitLog 是关键设计, commitLog 会保存已经消费的消息,不删任何消息,这样便支持重复消费,支持了重复消费,那么 同一个 Topic 就可以被多个 Consumer Group 消费 ,并以 commitLog 的设计为基础设计了事务消息。

注意: RocketMQ 并不是无限保留 CommitLog 消息 ,消费完成并过期的消息,会由 定期清理(Clean-up)机制 删除


消息层概念
**1) Message(消息):** 是 RocketMQ 中 **最基本的数据单元, **表示 业务系统需要传递的事件或数据, 主要包含三个部分:
  • 主题(Topic) : 消息逻辑分类,消费者按 Topic 消费
  • 消息体(Body) : 业务数据,通常是字节数组(例如 JSON、字符串)
  • 消息属性(Properties) : 元数据,可选,用于 Tag、Key、延迟、事务、过滤等

详细字段:

字段说明用途
Topic消息主题消费者订阅、分类
Body消息内容实际业务数据
Tag消息标签消费端可选择性订阅,用于消息过滤
messge Key消息唯一标识幂等、查询、追踪
DelayTimeLevel延迟级别延迟消息发送
BornTimestamp消息产生时间消息顺序/延迟计算
Properties扩展属性自定义字段,可用于业务逻辑

2) Tag (标签): 理解成消息的一个字段,消息的元信息,动态的随消息生成。多个topic可以有相同的tag,不影响。

3) message Key: 是消息的唯一标识符或 业务唯一标识**, **它是由生产者业务系统设置的标识 , 通常用于业务系统追踪或查询消息 , 可用于 **消息查询、幂等、日志追踪、统计, **Message Key 尽量唯一,但也可以多个消息共用一个 Key(例如同一个订单号)

RocketMQ 存储消息时,Key 仅作为 消息属性,实际消息定位仍靠 CommitLog + ConsumeQueue Offset

Message Key 可以唯一,也可以不唯一

  • 唯一 Key
    • 适合 业务系统要求严格幂等或精确查询的场景
    • 例如:订单号、支付流水号
    • 查询时通过 Key 能唯一定位消息
  • 非唯一 Key
    • 同一个业务对象可能发送多条消息,但 Key 可以相同
    • 例如:同一个用户多次操作,Key 是用户 ID
    • 查询时可以返回多条消息

4) 顺序消息: 消息的发送顺序与消费顺序保持一致。

RocketMQ实现顺序消息的关键在于 “同一业务的消息始终进入同一个 MessageQueue”,然后在消费端 同一时刻只由一个线程顺序消费该队列的消息。 整个过程依赖队列内天然的FIFO.


事务消息
**事务消息**:事务消息是一种将消息发送与本地事务绑定的高级消息类型。它确保消息的发送与本地事务的执行要么都成功,要么都失败,从而保证分布式场景下的数据一致性

本地事务指的是生产者服务开启了一个事务,操作包好发送消息给消息队列和本地的业务操作,如果发送消息给队列后,本地执行业务操作失败,那么消息最终不生效,被消息队列丢弃。

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

事务实现原理:

半消息状态:半消息会被标记一个特殊属性 PROPERTY_TRANSACTION_PREPARED,设置为 "true",事务消息包含的关键属性,半消息不会直接发送到指定的业务topic中,RocketMQ采用了 topic 隔离策略来存储半消息。

半消息 topic :所有的半消息都会被发送到一个名为 RMQ_SYS_TRANS_HALF_TOPIC 的内部系统 Topic 中,这个 Topic 是 RocketMQ 预留的,业务消费者不应该也无法订阅它,从而天然实现了消息的隔离和不可见

操作消息(OP消息):这是一个 RMQ_SYS_TRANS_OP_HALF_TOPIC,半消息提交或回滚的操作也叫做一条信息,存储在这里。因为 RocketMQ 的存储机制是基于 commitlog 顺序写 + ConsumeQueue 索引文件。 消息一旦写入,就不会修改(Append-only 模式)。 Broker 不能直接更新 Half Message 的状态, 所以采用“写一条对应操作记录”的方式来表达状态变化 ,这和数据库的“WAL 日志(Write-Ahead Log)”很像: 不改原数据,而是追加一条变更操作日志。

事务回查:rocketMQ 向 producer 询问当前的事务状态,producer 会检查本地事务是成功还是失败

定时任务: Broker 定期扫描 Half Queue,找出需要回查的消息

**Producer 回查接口 ** : 生产者实现业务逻辑检查点

在分布式系统中,最难的问题之一就是 跨系统的一致性,既 消息系统(MQ)和数据库事务无法保证原子性(Atomicity)。 RocketMQ 的 事务消息 正是为了解决这一点而生的。 它提供了一个“两阶段提交 + 回查机制”的模型,保证两者的最终一致性。

缺点:它只保证最终一致性,且中间状态(如支付成功但下游服务异常)在短暂时间内可能不一致,需要业务方自行处理。

Kafka、RabbitMQ 等常见 MQ 原生并没有类似机制 ,而这正是 RocketMQ 的亮点之一,国产之光!


延迟消息
**延迟消息(Scheduled Message)** 是指消息在发送到 Broker 后,不会立即被投递给消费者,而是**延迟一段时间后**再被消费。

实现原理:

  • 生产者形成延迟消息时 , 带上 delayLevel,Broker 收到后 不会立即写入真实 Topic
  • ** **Broker 写入“延迟队列”(定时队列) , 延迟队列的 Topic 固定为 SCHEDULE_TOPIC_XXXX
  • Broker 定时任务扫描到期消息 , 到期后将消息重新写入原始 Topic(即用户真正的 Topic)
  • 消费者收到消息 , 从原 Topic 消费到这条延迟后转发的消息

这跟RabbitMQ 的 TTL + DLQ 实现延迟队列思想一致。


死信消息
**死信消息**:指在消息消费失败,且达到**最大重试次数**后(默认16次)仍未成功,RocketMQ 会将这类消息转移到特殊的队列中进行隔离,这些消息被称为死信消息,存储死信消息的队列就是死信队列

死信队列:它是一个特殊的 Topic,名称通常为 **%DLQ% + 消费组ID**。每个消费组都有其对应的死信队列,用于存储该消费组内所有 Topic 的死信消息

死信队列是 RocketMQ 消息可靠性设计的最后一道防线,旨在隔离问题消息,防止其影响正常消息的处理流程,并提供一个集中的地方供监控和人工干预。

使用场景:死信队列主要用于处理消费失败且无法通过重试恢复的消息

  • 订单处理失败:订单创建、支付、库存扣减等操作失败的消息,可以进入死信队列,后续通过人工干预或自动程序进行补偿
  1. 异常消息处理:由于数据格式错误、业务逻辑异常等原因无法消费的消息,进入死信队列,供后续排查和修复。
  2. 超时消息处理:设置了 TTL 但未能在规定时间内消费的消息。
  3. 监控与告警:监控死信队列可以及时发现系统异常,是系统健康度的重要指标。

生产者工作原理
单生产者工作原理

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

  1. 创建并启动 Producer
    • 程序创建 **DefaultMQProducer** 实例。
    • 设置 NameServer 地址、Producer Group 名称。
    • 调用 **start()** 方法启动 Producer。
  2. 获取路由信息
    • Producer 启动后,会从一个 NameServer(随机选择)拉取 Topic 的路由信息。
    • 路由信息包含:该 Topic 分布在哪些 Broker 上,每个 Broker 上有哪些 MessageQueue(队列)。
    • Producer 会定时(默认30秒) 更新本地路由缓存。
  3. 选择 MessageQueue
    • Producer 根据负载均衡策略(如轮询 **RoundRobin**),从获取到的 MessageQueue 列表中选择一个队列。
    • 如果选择了写失败的队列,会进行重试,并可能暂时规避该队列。
  4. 构建并发送消息
    • 将消息体、Topic、Tag、Key 等信息封装成消息对象。
    • 对消息进行序列化、压缩等处理。
    • 通过 Netty 客户端向选定的 Broker 发送消息。
  5. Broker 处理消息
    • Broker 接收消息后,将其顺序写入 CommitLog 文件
    • 写入成功后,返回一个包含消息物理偏移量(CommitLog Offset)的 ACK 给 Producer。
  6. Broker 异步构建索引
    • Broker 的后台线程会异步地将 CommitLog 中的消息位置、大小等信息分发到对应的 ConsumeQueue 文件。
    • 如果消息带有 Key,还会构建 IndexFile 索引,以支持按 Key 查询。
  7. Producer 接收 ACK
    • Producer 收到 Broker 返回的 **SendResult**,状态为 **SEND_OK**,表示消息发送成功。

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

重点说明:

  • 异步构建索引:消息写入 CommitLog 和构建 ConsumeQueue 索引是异步的。这极大地提升了 Broker 的吞吐性能,因为写入 CommitLog 是顺序写,非常快,而构建索引可以稍后批量处理。
  • 高可用设计:如果发送失败,Producer 会自动重试(默认2次),并可能选择其他 Broker 上的队列,实现了容错。

生产者群组工作原理

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

1. 启动与路由发现

  • 拉取路由:当 Producer Group 中的任何一个实例启动时,它会定期向 NameServer 拉取它所关心的 Topic 的路由信息(包括该 Topic 分布在哪些 Broker 上,以及每个 Broker 上的 MessageQueue 列表)。
  • 心跳机制:Producer 实例会与 NameServer 和 Broker 保持心跳,但这主要是为了让它们感知自己的存活状态,而非为了“注册”。
  • 无状态设计:因此,Producer 实例本身是无状态的,Broker 也只关心消息内容,不关心消息具体来自哪个实例。这使得 Producer Group 内的实例可以任意增减,实现弹性伸缩。

2. 消息发送(正常运行)

在发送消息时,Producer Group 的工作机制体现了其高扩展性:

  • 独立发送:每个 Producer 实例都独立工作,根据其本地缓存的路由信息,通过负载均衡策略(如轮询)从 Topic 的多个 MessageQueue 中选择一个。
  • 并行处理:然后,它将消息直接发送给该 MessageQueue 所在的 Broker。多个实例可以并行地向不同的 Broker 或不同的 Queue 发送消息,共同承担发送压力,实现了负载均衡。

3. 事务消息回查(容错机制)

在事务消息场景下,Producer Group 的设计提供了关键的容错能力,这也是其核心价值之一:

  • 问题场景:如果某个 Producer 实例在发送“半消息”后、提交最终状态(COMMIT/ROLLBACK)前突然宕机,Broker 上就会留下一个状态不明的“孤儿”半消息。
  • 回查机制:此时,Broker 不会主动“通知” 同组的其他实例。正确的流程是,Broker 会主动发起回查。它会根据半消息中记录的 Producer Group 名称,向 NameServer 查询该 Group 下任意一个存活的 Producer 实例地址
  • 状态恢复:然后,Broker 向这个被选中的实例发起回查请求,由它代表整个 Group 去查询本地事务状态(如查询数据库),并将结果(COMMIT 或 ROLLBACK)返回给 Broker。Broker 根据这个结果完成最终的事务提交或回滚。

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传


消费者工作原理
在深入两种模式之前,先理解几个贯穿始终的核心概念:
  • Consumer Group(消费者组):一个逻辑概念,由多个消费者实例组成。它们共同消费一个或多个 Topic,是实现负载均衡和容错的基本单元。
  • Rebalance(重平衡):一个动态过程,当消费者组内的实例发生变化(加入、离开)或 Topic 的队列数量变化时,Broker 会重新分配队列与消费者的对应关系。
  • Offset 管理:记录消费进度。这是区分两种模式的关键所在。

1)集群模式

集群模式是默认且最常用的模式,旨在通过横向扩展来提升消费能力。

  1. 队列分配:一个 Topic 的多个 MessageQueue 会被分配给消费者组内的不同实例。一个队列在同一时间只会被组内的一个消费者实例消费
  2. 负载均衡:通过 Rebalance 机制,如果消费者实例数量多于队列数量,多余的实例会闲置;如果实例数量少于队列数量,一个实例会消费多个队列。这实现了消费任务的负载均衡。
  3. 故障转移:如果一个消费者实例宕机,它负责的队列会被 Rebalance 机制重新分配给其他存活的实例,确保消息消费不中断。
  4. Offset 存储:由于消费进度需要被组内所有实例共享(尤其是在故障转移时),消费进度(Offset)集中存储在 Broker 端

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

2)广播模式

广播模式适用于需要将同一条消息推送给所有下游消费者的场景,如配置更新、状态通知等。

  1. 全局消费:消费者组内的每个实例都会消费 Topic 下所有 MessageQueue 的所有消息。消息不会被分摊,而是被广播。
  2. 无 Rebalance 分配:由于每个消费者都要消费所有队列,因此不存在队列分配的 Rebalance 过程。每个消费者独立地订阅所有队列。
  3. 独立消费:各消费者实例的消费进度互不影响,一个实例消费慢或失败,不影响其他实例。
  4. Offset 存储:由于每个消费者的消费进度都是独立的,不需要与其他实例同步,因此消费进度(Offset)存储在消费者本地

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

两种模式对比:

特性维度集群模式广播模式
消费关系一个队列只被一个消费者实例消费一个队列被所有消费者实例消费
负载均衡支持,通过 Rebalance 在组内分摊消费任务不支持,每个实例承担全部消费负载
故障转移支持,宕机实例的队列会被其他实例接管不支持,实例间完全独立,互不影响
Offset 存储Broker 端集中存储消费者本地独立存储
适用场景高吞吐量、分摊消费压力的业务(如订单处理、日志分析)配置下发、状态通知、所有客户端需要同步收到相同消息的场景

kafka 消息队列概念汇总

类别概念作用类比与说明
核心组件Producer消息生产者,负责发送消息到 Kafka Broker。消息的来源,可以是网站前端、后端服务、日志采集器等。
Consumer消费者,从 Kafka 读取消息。消息的终点,负责处理消息,如数据分析、业务处理等。
BrokerKafka 服务器实例,负责存储和转发消息。一个 Kafka 集群由多个 Broker 组成,每个 Broker 都是一个独立的节点。
Topic消息分类主题,是逻辑上的消息分类。类似数据库中的表名,用于区分不同类型的消息(如订单、用户行为)。
PartitionTopic 下的分区,是真正存储消息的地方。一个 Topic 可以分为多个 Partition,分布在不同 Broker 上,实现水平扩展和并行处理。类比 RocketMQ 的 MessageQueue
Offset每条消息在 Partition 中的唯一序号。Partition 内部消息的指针,从 0 开始单调递增。类比 RocketMQ 的 Message Queue Offset
Consumer Group一组消费者,负责负载均衡消费。一个 Topic 的消息可以被多个不同的 Consumer Group 订阅,每个 Group 都有独立的消费进度。
Leader / FollowerPartition 的主从副本,保证高可用。每个 Partition 有一个 Leader 负责读写,多个 Follower 负责同步数据。类比 RocketMQ 的 Master/Slave
Zookeeper / Kafka Controller管理 Broker、Topic、Partition 元数据。旧版依赖 Zookeeper新版(KRaft)用 Controller 代替,Controller 是从 Broker 中选举出来的,简化了架构。类比 RocketMQ 的 NameServer
生产者端acks生产者可靠性核心配置,决定何时认为消息发送成功。**acks=0**
(发完即成功), **acks=1**
(Leader收到即成功), **acks=all**
(所有副本收到才成功)。
幂等性生产者保证单分区单会话内 Exactly-Once,防止重试导致消息重复。开启后,Producer 为每条消息分配唯一ID,Broker 去重,确保消息不重复。
事务性生产者保证跨分区、跨主题的原子写入,实现“要么都成功,要么都失败”。结合幂等性和事务协调器,实现 Kafka 的 Exactly-Once 语义 (EOS)。
存储与 BrokerLog SegmentPartition 内的日志文件片段。一个 Partition 的日志由多个 Log Segment 组成,方便日志滚动和清理。类比 RocketMQ CommitLog 的切分文件
Log 结构Kafka 高性能的基石,Partition 是一个只追加的、有序的日志文件。顺序写磁盘,避免了随机写的巨大开销,性能极高。
零拷贝消费者高性能的关键,数据在内核态直接从磁盘发送到网卡。避免了数据在用户空间和内核空间之间的多次拷贝,极大提升了吞吐量。
索引加速消息查找,包括 Offset 索引和时间戳索引。允许消费者根据 Offset 或时间戳快速定位消息,而不必遍历整个日志。
Retention Policy消息保留策略,按时间或大小删除旧消息。Kafka 的清理机制,防止磁盘被占满,如保留7天或达到10GB大小。
消费者端Rebalance消费者组内分区分配的动态过程,是负载均衡和故障转移的基础。当组内消费者数量或 Topic 分区数量变化时触发,期间消费会短暂停止。
提交机制决定消费语义的关键,消费者如何告知 Broker 自己的消费进度。分为自动提交、手动同步提交(可靠但慢)、手动异步提交(快但需处理回调)。
消费语义消息传递的保证级别。At-Most-Once (最多一次), At-Least-Once (至少一次,默认), Exactly-Once (精确一次)。
位移主题消费位移的存储位置。Kafka 将消费组的 Offset 信息存储在内部的 Topic **__consumer_offsets**
中,而非 Broker 文件。
集群协调与高可用ISR (In-Sync Replica)副本同步集合,确保消息可靠复制。与 Leader 保持同步的 Follower 集合。acks=all 时,Leader 必须等待 ISR 中所有副本确认。类比 RocketMQ 的 Master-Slave 同步机制
KRaft 模式Kafka 的未来方向,移除 Zookeeper 依赖。使用 Raft 协议在 Broker 内部选举 Controller,简化部署和运维,提升性能。
Cooperative Rebalance更平滑的 Rebalance 协议,解决 Rebalance 时的“Stop-the-World”问题。分区可以增量地、分批地重新分配,消费中断更短,体验更平滑。
高级特性消息压缩节省网络带宽和磁盘空间。Producer 可在发送前对消息进行压缩(如 Gzip, Snappy),Broker 和 Consumer 自动解压。
安全机制保障数据安全。包括 SSL/TLS (加密传输)、SASL (身份认证)、ACL (权限控制)。

kafka VS RocketMQ 能力对比

特性KafkaRocketMQ
系统类型分布式消息中间件分布式消息中间件
核心功能异步解耦、削峰填谷、异步通信异步解耦、削峰填谷、事务消息
典型角色Producer / Broker / ConsumerProducer / Broker / Consumer
数据模型Topic → Partition → OffsetTopic → MessageQueue → Offset
消费模式基于 Pull(拉取)基于 Pull(主动拉)
消息投递“至少一次” (At least once)“至少一次”,支持事务一致性
支持事务有(Kafka Transaction)有(RocketMQ Transaction Message)
是否支持本地事务❌ 否。Kafka 事务只管消息投递原子性✅ 是。业务代码可以和消息发送绑定
存储方式Partition 文件(日志结构)CommitLog + ConsumeQueue
持久化机制Append-only + LogSegmentCommitLog 顺序写
Offset 管理Consumer 自管(可提交)Broker 统一维护(集群模式)
路由发现Controller(或 ZooKeeper)NameServer
高可用ISR 同步副本机制主从 + 同步复制机制
消息清理TTL 或磁盘大小策略定期清理 CommitLog
应用方向大数据、日志流处理、实时分析分布式系统、事务一致性、业务异步化

本地事务: 不是消息队列的事务,而是 你自己服务内部 的那部分业务逻辑(mysql,Spring事务等)。 RocketMQ 可以做到 让 本地事务消息发送 的结果保持一致。 Kafka做不到。

三个消息队列对比

| 特性维度 | RabbitMQ | RocketMQ | Kafka | | --- | --- | --- | --- | | **架构模型** | **中心化 Broker** (AMQP 协议) | **分布式消息平台** | **分布式日志系统** | | **核心设计目标** | **智能消息路由**和**可靠传递** | **金融级可靠性**和**海量消息**的强一致性 | **高吞吐量**的**实时数据流处理** | | **消息模型** | **Exchange -> Queue** 模型,路由灵活 | **Topic -> MessageQueue** 模型,借鉴 Kafka | **Topic -> Partition** 模型,简单直接 | | **性能吞吐** | 中等(万级/秒) | **高**(十万级/秒) | **极高**(百万级/秒) | | **可靠性** | 高(通过 ACK、持久化、镜像队列) | **极高**(同步/异步刷盘、事务消息、主从复制) | 高(通过多副本 ISR 机制) | | **顺序性** | **队列内有序**(多消费者会打乱) | **队列内有序**(严格顺序消息) | **分区内有序**(全局无序) | | **消息回溯** | 不支持(需插件) | **原生支持**(按 Offset 或时间) | **原生支持**(按 Offset 或时间) | | **事务消息** | **不支持**(可通过 AMQP 事务模拟,性能差) | **原生支持**(两阶段提交+状态回查) | **不支持**(需借助外部系统,如 Kafka Streams) | | **生态系统** | 成熟,支持多种协议 | 主要在 Java 生态,阿里系生态完善 | **极其庞大**(流处理、大数据生态) | | **运维复杂度** | **较低**(集群搭建相对简单) | **中等**(Java 技术栈,对国内团队友好) | **较高**(依赖 Zookeeper,配置复杂) | | **典型应用场景** | **微服务通信**、**业务事件驱动**、**任务队列** | **订单交易**、**金融支付**、**大规模互联网应用** | **日志收集**、**大数据管道**、**实时监控** |

哪个消息队列应用最广泛?

从全球范围来看,Kafka 和 RabbitMQ 的应用广度远超 RocketMQ。RocketMQ在国内应用广泛,毕竟起源于国内,专为 Java 生态而生。

  1. Kafka:大数据领域的绝对霸主
    • 大数据、日志聚合、流式计算领域,Kafka 是事实上的标准。几乎所有的大数据组件(Flink, Spark, Storm)都与 Kafka 深度集成。
    • 如果你的场景是处理海量的数据流,Kafka 几乎是首选。在这个领域,它的应用最广泛。
  2. RabbitMQ:企业级应用和微服务的常青树
    • 传统企业应用、微服务架构、业务系统集成方面,RabbitMQ 凭借其成熟稳定、灵活的路由和丰富的协议支持,拥有巨大的用户基数。
    • 很多公司的第一个消息队列就是 RabbitMQ,因为它上手快,能解决大部分业务解耦和异步通信问题。在通用业务集成领域,它的应用非常广泛。
  3. RocketMQ:亚洲和大型互联网公司的利器
    • RocketMQ 起源于阿里,在国内的大型互联网公司、金融、电商领域应用极为广泛。它经历了“双十一”等超大规模场景的考验。
    • 它的优势在于对金融级事务消息、顺序消息、海量消息堆积等复杂场景的完美支持。
    • 不过,在欧美市场,RocketMQ 的知名度和采用率远不及 Kafka 和 RabbitMQ。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值