欢迎光临
我们一直在努力

RabbitMQ篇章-从核心概念到七大工作模式认识RabbitMQ

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */
#content_views .toc,
/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */
#content_views.markdown_views > p:empty:has(+ .toc),
#content_views.markdown_views > .toc + p:empty,
/* 富文本旧版目录标记 */
#content_views.htmledit_views #main-toc,
#content_views.htmledit_views #hr-toc,
#content_views.htmledit_views p[id*=\”-toc\”] {
display: none !important;
}
/* 目录去掉后,紧跟的首个标题不再多出一块上边距 */
#content_views.markdown_views > .toc + h1,
#content_views.markdown_views > .toc + h2,
#content_views.markdown_views > .toc + h3,
#content_views.markdown_views > .toc + h4,
#content_views.markdown_views > .toc + p:empty + h1,
#content_views.markdown_views > .toc + p:empty + h2,
#content_views.markdown_views > .toc + p:empty + h3,
#content_views.markdown_views > .toc + p:empty + h4 {
margin-top: 0 !important;
}

RabbitMQ 详解:从核心概念到七大工作模式

在分布式系统中,服务之间的通信永远绕不开一个问题:调用方和被调用方,如何优雅地对话? 本文基于课程资料整理,先讲清楚 什么是 RabbitMQ、它的核心概念,再逐一剖析 RabbitMQ 的七大工作模式,每种模式都配有结构示意图,帮你一次学透。


目录

  • 一、什么是 MQ
  • 二、什么是 RabbitMQ
  • 三、RabbitMQ 的安装
  • 四、RabbitMQ 核心概念
  • 五、RabbitMQ 的七大工作模式
    • 1. Simple 简单模式
    • 2. Work Queue 工作队列模式
    • 3. Publish/Subscribe 发布订阅模式
    • 4. Routing 路由模式
    • 5. Topics 通配符模式
    • 6. RPC 通信模式
    • 7. Publisher Confirms 发布确认模式
  • 六、七种模式对比总结

一、什么是 MQ

MQ(Message Queue,消息队列),从字面意思看,本质就是一个队列——FIFO(先进先出),只不过队列中存放的内容是消息(message)而已。消息可以非常简单,比如只包含一个文本字符串、JSON;也可以很复杂,比如内嵌对象。

MQ 多用于分布式系统之间进行通信。

1.1 系统间通信的两种方式

系统之间的调用通常有两种方式:

方式说明特点
同步通信 直接调用对方的服务,数据从一端发出后立即就可以达到另一端 实时性强,但调用方需要阻塞等待,耦合度高
异步通信 数据从一端发出后,先进入一个容器进行临时存储,当达到某种条件后,再由这个容器发送给另一端 削峰填谷、解耦,这个"容器"的典型实现就是 MQ

RabbitMQ 就是 MQ 的一种实现。

1.2 MQ 的作用

可以把 MQ 想象成一个仓库:采购部进货之后,把零件放进仓库里,生产部从仓库中取出零件加工成产品。MQ 和仓库的区别是——仓库里放的是物品,MQ 里放的是消息;仓库负责存储和转发物品,MQ 负责存储和转发消息。

具体来说,MQ 主要有以下作用:

  • 异步解耦:业务流程中一些非常耗时、但不需要即时返回结果的操作,可以借助 MQ 异步化。例如用户注册后发送短信/邮件通知,可以作为异步任务处理,不必等这些操作完成才告知用户注册成功。
  • 流量削峰:访问量剧增时(如秒杀、促销),如果以峰值为标准投入资源无疑是巨大的浪费。使用 MQ 将请求排队,系统根据自己的处理能力逐步消费,关键组件就不会被突发流量压垮。
  • 消息分发:当多个系统需要对同一数据做出响应时,可以用 MQ 进行消息分发。例如支付成功后,支付系统向 MQ 发消息,其他系统订阅该消息即可,无需轮询数据库。
  • 延迟通知:需要在特定时间后发送通知的场景,可以使用 MQ 的延迟消息功能。例如电商平台中,用户下单一定时间未支付,超时后自动取消订单。

  • 二、什么是 RabbitMQ

    RabbitMQ 是采用 Erlang 语言实现 AMQP 协议的消息中间件,它最初起源于金融系统领域,是为了在分布式系统中存储和转发消息而设计的。

    先解释两个名词:

    • Rabbit:兔子,也是一个公司名(Rabbit Technologies)。之所以取名为 Rabbit,是因为兔子行动非常迅速且繁殖起来非常"疯狂"——创始团队认为这个名字再合适不过了。
    • MQ:Message Queue,消息队列。RabbitMQ 就是 Rabbit 公司旗下的一个消息队列产品。

    AMQP,即 Advanced Message Queuing Protocol(高级消息队列协议),是一个通用的应用层协议,为面向消息的中间件设计。基于此协议的客户端与消息中间件可传递消息,并不受客户端/中间件、开发语言等条件的限制。

    2.1 为什么选择 RabbitMQ

    目前业界有很多 MQ 产品:RabbitMQ、RocketMQ、ActiveMQ、Kafka、ZeroMQ 等,也有直接用 Redis 充当消息队列的案例。这些消息队列各有侧重,没有绝对的好坏,只有适合不适合。选型时需要结合自身需求和产品特征综合考虑。

    当前最主流的 3 种 MQ 产品对比:

    产品开发语言特点适合场景
    Kafka Scala/Java 追求高吞吐量,单机吞吐达十万级,在日志领域比较成熟,功能较为简单 日志采集、大数据流式处理(有日志采集需求首选 Kafka)
    RocketMQ Java(阿里开源,后捐赠 Apache) 借鉴 Kafka 并做了改进,可靠性、稳定性出色;但客户端语言支持不多,社区活跃度一般 可靠性要求高、并发量大的场景,如互联网金融
    RabbitMQ Erlang MQ 功能较完备,几乎支持所有主流语言,管理界面非常友好,吞吐量万级,社区活跃度高 中小型公司、数据量没那么大且并发没那么高的场景

    💡 综合来看:RabbitMQ 综合能力较强,社区比较成熟、管理界面友好,因此学习消息中间件,RabbitMQ 是非常好的入门选择。

    2.2 发展历史

    • 2006 年,RabbitMQ 开发始于 Rabbit Technologies 公司;
    • 2010 年 4 月,Rabbit Technologies 被 VMware 旗下的 SpringSource 收购;
    • 2013 年 5 月,被并入 Pivotal(我们现在常用的 Spring 系列框架,就是 Pivotal 公司的门户产品之一);
    • 后来 Pivotal 将其开源,RabbitMQ 才逐渐走向大众,如今在易用性、扩展性、可靠性和高可用性等方面都有卓越表现,被互联网公司大规模应用。

    三、RabbitMQ 的安装

    RabbitMQ 基于 Erlang 语言编写,所以安装 RabbitMQ 之前,需要先安装部署 Erlang 环境,再安装 RabbitMQ。RabbitMQ 大多部署在 Linux 操作系统上。

    3.1 Ubuntu 环境安装(学习阶段推荐)

    安装过程分为四步:

  • 安装 Erlang:先更新软件包,然后通过系统包管理器安装 Erlang,安装完成后可以用命令行交互界面查看版本(退出命令为 halt().);
  • 安装 RabbitMQ:更新软件包后安装 rabbitmq-server,再用 systemctl status rabbitmq-server 确认安装结果,看到服务处于 active (running) 状态即说明安装成功;
  • 开启管理界面:RabbitMQ 默认不安装管理界面,需要手动启用 rabbitmq_management 插件;
  • 启动服务并访问:启动服务后,浏览器访问 http://IP:15672(15672 是管理界面的默认端口,云服务器需要提前开放该端口)。
  • 💡 默认用户名和密码都是 guest。但 RabbitMQ 从 3.3.0 开始禁止使用 guest/guest 权限通过除 localhost 以外的访问,所以建议添加一个管理员用户:先用添加用户命令创建账号(如 admin),再用设置角色命令将其赋予管理员角色。

    RabbitMQ 用户角色共六种:Administrator(超级管理员)、Monitoring(监控者)、Policymaker(策略制定者)、Management(普通管理者)、Impersonator、None(普通生产者/消费者)。

    💡 另一种更简单的方式——Docker 安装:直接拉取带管理界面的 rabbitmq 镜像并运行容器,同时映射 5672(客户端通信端口)和 15672(管理界面端口)两个端口即可。


    四、RabbitMQ 核心概念

    4.1 一个比喻:邮局模型

    RabbitMQ 是一个消息中间件,也是一个生产者消费者模型,它负责接收、存储并转发消息。

    消息传递的过程非常类似邮局:

    • 当你要发送一封邮件时,你把邮件放到邮局,邮局接收邮件,并通过邮递员送到收件人的手上。
    • 按照这个逻辑:Producer 就类似邮件发件人,Consumer 就是收件人,RabbitMQ 就类似于邮局。

    4.2 核心概念详解

    (1)Producer / Consumer / Broker
    概念说明
    Producer(生产者) RabbitMQ Server 的客户端,向 RabbitMQ 发送消息。消息通常是带有业务逻辑结构的数据(如 JSON 字符串),并可以携带标签,RabbitMQ 根据标签进行路由
    Consumer(消费者) 也是 RabbitMQ Server 的客户端,从 RabbitMQ 接收消息。消费过程中标签会被丢掉,消费者只收到消息,并不知道(也不需要知道)生产者是谁
    Broker(代理节点) 就是 RabbitMQ Server 本身,负责接收和收发消息。一个 Broker 可以简单看作一个 RabbitMQ 服务节点/实例,大多数情况下也可以看作一台 RabbitMQ 服务器
    (2)Connection 和 Channel
    • Connection(连接):客户端和 RabbitMQ 服务器之间的一个 TCP 连接,是建立消息传递的基础,负责传输所有数据和控制信息。
    • Channel(通道/信道):Connection 之上的一个抽象层。一个 TCP 连接可以有多个 Channel,每个 Channel 都是独立的虚拟连接,消息的发送和接收都是基于 Channel 进行的。

    💡 Channel 的主要作用是把消息的读写操作复用到同一个 TCP 连接上,减少建立和关闭连接的开销,提升性能。如果每次收发消息都建立一条 TCP 连接,开销太大了。

    (3)Virtual Host(虚拟主机)

    Virtual host 是一个虚拟概念,为消息队列提供了逻辑上的隔离机制。一个 Broker 上可以存在多个 Virtual Host,当多个不同的用户使用同一个 RabbitMQ Server 时,可以划分出多个 vhost,每个用户在自己的 vhost 中创建交换机、队列等资源。

    💡 类比 MySQL 的 database:一个 MySQL 服务器可以有多个 database,一个 RabbitMQ Broker 可以有多个 vhost。

    (4)Queue(队列)

    Queue 是 RabbitMQ 的内部对象,用于存储消息。多个消费者可以订阅同一个队列。

    (5)Exchange(交换机)

    Exchange 是 message 到达 Broker 的第一站,它负责接收生产者发送的消息,并按照特定规则把消息路由到一个或多个 Queue 中。

    💡 类比快递物流:发快递之后,物流公司根据地址把快递分派到不同的站点,再送到收件人手上——这个"分拣"的工作就是交换机做的。

    RabbitMQ 交换机有四种类型,不同类型有着不同的路由策略:

    类型路由策略对应工作模式
    Fanout 广播,把消息交给所有绑定到交换机的队列 Publish/Subscribe 模式
    Direct 定向,把消息交给符合指定 routing key 的队列 Routing 模式
    Topic 通配符,把消息交给符合 routing pattern 的队列 Topics 模式
    Headers 不依赖路由键,根据消息内容中的 headers 属性匹配 性能差、不实用,基本不会用到

    ⚠️ 重要:Exchange 只负责转发消息,不具备存储消息的能力。如果没有任何队列与 Exchange 绑定,或者没有符合路由规则的队列,那么消息就会丢失!

    (6)RoutingKey 与 BindingKey
    • RoutingKey(路由键):生产者将消息发给交换器时指定的一个字符串,告诉交换机该如何处理这个消息。
    • BindingKey(绑定键):通过 Binding(绑定) 将交换器与队列关联起来,绑定的时候一般会指定一个 BindingKey,RabbitMQ 就知道如何正确地将消息路由到队列了。

    两者可以这样理解:

  • 绑定队列和交换机的时候,需要的路由键是 BindingKey;
  • 发送消息的时候,需要的路由键是 RoutingKey。
  • 多数时候(包括官方文档和 Java API)都把两者统称为 Routing Key,根据使用场景区分即可。

    4.3 RabbitMQ 工作流程

    把上面的概念串起来,RabbitMQ 的完整工作流程是:

  • Producer 生产了一条消息;
  • Producer 连接到 RabbitMQ Broker,建立 Connection、开启 Channel;
  • Producer 声明 Exchange(交换机,路由消息);
  • Producer 声明 Queue(队列,存放消息);
  • Producer 发送消息至 RabbitMQ Broker;
  • RabbitMQ Broker 接收消息,并存入相应的 Queue 中;如果未找到相应的队列,则根据生产者的配置,选择丢弃或者退回给生产者。 在这里插入图片描述
  • 4.4 快速入门:Hello World

    使用 Java 原生客户端体验最简单的收发消息,只需要引入官方的 amqp-client 依赖。

    生产者端的编程套路可以总结为"五步曲":

  • 创建连接工厂:设置服务器 IP(默认 localhost)、端口(默认 5672)、虚拟主机(默认 /)、用户名密码(默认 guest/guest);
  • 创建连接和信道:由工厂创建 Connection,再从 Connection 中开启 Channel;
  • 声明队列:指定队列名、是否持久化、是否独占、是否自动删除等参数——如果队列不存在会自动创建,存在则不创建;
  • 发送消息:指定交换机、RoutingKey、配置项和消息数据。使用内置交换机(名为空字符串)时,RoutingKey 要和队列名称一致,才能路由到对应队列;
  • 释放资源:依次关闭信道和连接。
  • 消费者端的前三步与生产者相同,第 4 步改为消费消息:调用消费方法时传入队列名、是否自动确认和一个回调对象,收到消息后回调方法会自动执行,打印消息内容即可。消费者相当于一个监听程序,一般不需要关闭资源。

    运行后,消费者控制台输出接收到的消息内容,这就是 RabbitMQ 的简单模式。


    五、RabbitMQ 的七大工作模式

    RabbitMQ 官方提供了 7 种工作模式,入门程序的案例其实就是其中的"简单模式"。官方文档:RabbitMQ Tutorials

    1. Simple(简单模式)

    在这里插入图片描述

    • P:生产者,发送消息的程序;
    • C:消费者,消息的接收者;
    • Queue:消息队列(黄色部分),类似一个邮箱,可以缓存消息;生产者向其中投递消息,消费者从其中取出消息。

    特点:一个生产者 P,一个消费者 C,消息只能被消费一次,也称为点对点(Point-to-Point)模式。

    适用场景:消息只能被单个消费者处理。

    (实现方式见上文 4.4 快速入门)


    2. Work Queue(工作队列模式)

    在这里插入图片描述

    一个生产者 P,多个消费者 C1、C2…… 在有多个消息的情况下,Work Queue 会将消息分派给不同的消费者,每个消费者都会接收到不同的消息。

    特点:消息不会重复,消费者之间是竞争关系,每个消息只能被一个消费者接收。

    适用场景:集群环境中做异步处理。例如 12306 短信通知服务——订票成功后订单消息发送到 RabbitMQ,多个短信服务实例从 RabbitMQ 中获取订单信息并发送通知,相当于在短信服务之间进行任务分配。

    实现思路:生产者代码和简单模式基本一样,为了看到多个消费者竞争的关系,循环发送 10 条消息即可;消费者代码复制两份(内容可以完全一样)。运行后 10 条消息被轮流分配给了两个消费者(一个收到偶数序号、一个收到奇数序号),没有任何一条被重复消费。

    💡 测试技巧:要先启动两个消费者,再启动生产者。如果先启动生产者,由于消息较少处理较快,第一个启动的消费者会瞬间把 10 条消息全部消费掉。


    3. Publish/Subscribe(发布/订阅模式)

    在这里插入图片描述

    在订阅模型中,多了一个角色 Exchange(交换机,图中 X),过程略有变化:

    • 生产者不再直接把消息投递到队列(实际上这个在 RabbitMQ 中不会发生),而是发送到 Exchange;
    • 由交换机按一定规则将消息路由到一个或多个队列中;
    • 一条消息经过交换机被复制成多份,每个消费者接收相同的消息。

    特点:生产者发送一条消息,经过交换机转发到多个不同的队列,多个不同的队列就有多个不同的消费者。

    适用场景:消息需要被多个消费者同时接收的场景,如实时通知或广播消息。例如中国气象局发布"天气预报"消息送入交换机,新浪、百度、搜狐、网易等门户网站将各自队列绑定到该交换机,即可自动获取气象数据。

    实现思路(Fanout 类型交换机):与前两种模式的区别是需要创建交换机,并绑定队列和交换机——先创建 fanout 类型的交换机(可指定是否持久化、是否自动删除等参数),再声明两个队列,然后把队列和交换机绑定(fanout 类型 RoutingKey 设置为空字符串,表示所有绑定队列都能收到全部消息),最后把消息发送到交换机。运行后两个队列各收到一条消息,两个消费者都打印了相同的内容。


    4. Routing(路由模式)

    在这里插入图片描述

    路由模式是发布订阅模式的变种,在发布订阅基础上增加了路由 key:

    • 队列和交换机绑定的时候,不能任意绑定了,要指定一个 BindingKey;
    • 消息发送方向 Exchange 发送消息时,也需要指定 RoutingKey;
    • Exchange 根据消息的 RoutingKey 进行判断,只有队列绑定的 BindingKey 和消息的 RoutingKey 完全一致,该队列才会接收到消息。

    区别:发布订阅模式是无条件地将所有消息分发给所有消费者;路由模式是 Exchange 将数据筛选后发给对应的消费者队列。

    适用场景:需要根据特定规则分发消息的场景。例如系统打印日志,日志等级分为 error、warning、info、debug,可以把不同的日志发送到不同的队列,最终输出到不同的文件。

    实现思路(Direct 类型交换机):和发布订阅模式的区别就是交换机类型不同、绑定队列的 RoutingKey 不同——创建 direct 类型交换机后,队列 1 只绑定 orange,队列 2 绑定 black 和 green(一个队列可以绑定多个 BindingKey)。发送消息时指定不同的 RoutingKey,最终队列 1 只收到 orange 的消息,队列 2 收到 black 和 green 的消息。


    5. Topics(通配符模式)

    在这里插入图片描述

    路由模式的升级版,在 RoutingKey 的基础上增加了通配符功能,使之更加灵活。

    Topics 和 Routing 的基本原理相同,不同之处在于 RoutingKey 的匹配方式:

    • Routing 模式是相等匹配(完全一致);
    • Topics 模式是通配符匹配(类似正则表达式的思路)。

    匹配规则:

  • RoutingKey 是一系列由点(.)分隔的单词,如 stock.usd.nyse、quick.orange.rabbit;
  • BindingKey 和 RoutingKey 一样,也是点分割的字符串;
  • BindingKey 中可以存在两种特殊字符串用于模糊匹配:
    • *:表示一个单词;
    • #:表示多个单词(0~N 个)。
  • 适用场景:需要灵活匹配和过滤消息的场景。

    举例(Topic 类型交换机):队列 1 只绑定 *.error(仅接收 error 信息),队列 2 绑定 #.info 和 *.error(error、info 都接收)。发送 order.error 时两个队列都收到;发送 order.pay.info 时只有队列 2 收到——因为它有三段单词,不匹配两段的 *.error,但能匹配 #.info(# 匹配多段)。

    消息 RoutingKeyqueue1(*.error)queue2(#.info + *.error)
    order.error ✅ 匹配 *.error ✅ 匹配 *.error
    order.pay.info ❌ ✅ 匹配 #.info
    pay.error ✅ ✅

    💡 如果消息的 RoutingKey 与任何 BindingKey 都不匹配,消息会被丢弃,或者退回给生产者(需要设置 mandatory 参数)。


    6. RPC(远程过程调用)

    RPC(Remote Procedure Call),即远程过程调用,是一种通过网络从远程计算机上请求服务、而不需要了解底层网络技术的技术,类似于 HTTP 远程调用。

    在 RPC 通信的过程中,没有生产者和消费者之分,比较像我们平时使用的 RPC 远程调用——通过两个队列实现了一个可回调的过程:

    在这里插入图片描述

    流程:

  • 客户端发送消息到一个指定的队列,并在消息属性中设置 replyTo 字段——该字段指定了一个回调队列,用于接收服务端的响应;
  • 服务端接收到请求后,处理请求,并把响应消息发送到 replyTo 指定的回调队列;
  • 客户端在回调队列上等待响应消息。一旦收到响应,客户端会检查消息的 correlationId 属性,以确保它是所期望的响应。
  • 客户端实现思路:声明请求队列和一个临时回调队列;生成本次请求的唯一标志(如 UUID),连同回调队列名一起设置到消息属性中,通过内置交换机发出请求;然后用阻塞队列监听回调队列——收到响应后先校验 correlationId 与本次请求是否匹配,匹配才放入阻塞队列,主线程从阻塞队列取出结果,从而实现"发出请求 → 等待响应"的同步效果。

    服务端实现思路:声明同一个请求队列;通过 QoS 设置同时最多只能获取一个消息(保证一个请求对应一个响应,否则默认情况下消费者可能同时收到多条消息);收到请求后进行处理,把响应消息发送到请求属性中 replyTo 指定的回调队列(响应中携带原请求的 correlationId,方便客户端匹配),最后手动对消息进行应答。

    💡 关于 autoAck(消息确认机制):消费方法的 autoAck 参数指定消费者是否自动向队列确认消息。

    • autoAck=true(自动确认):消息发给消费者后立即从内存中删除,如果消费者处理失败,消息就丢了;
    • autoAck=false(手动确认):消费者需要显式调用确认方法,消息队列才删除消息。可靠性更高,适用于消息处理重要且需要确保每个消息被正确处理的场景。

    7. Publisher Confirms(发布确认)

    作为消息中间件,都会面临消息丢失的问题。消息丢失大概分为三种情况:

  • 生产者问题:因应用程序故障、网络抖动等原因,生产者没有成功向 Broker 发送消息;
  • 消息中间件自身问题:生产者成功发送给了 Broker,但 Broker 没有把消息保存好,导致丢失;
  • 消费者问题:Broker 发消息给消费者,消费者没有处理好,导致 Broker 将消费失败的消息从队列中删除了。
  • 针对这三种问题,RabbitMQ 给出了对应的解决方案:

    丢失环节解决方案
    生产者 → Broker 发布确认(Publisher Confirms)
    Broker 自身 持久化机制
    Broker → 消费者 消息应答机制(上文 RPC 中提到的手动 ack)

    在这里插入图片描述

    发布确认是 RabbitMQ 七大工作模式之一,属于 AMQP 0.9.1 协议的扩展,默认不开启,需要将信道设置为 confirm 模式。

    工作原理:

  • 信道进入 confirm 模式后,所有在该信道上发布的消息都会被指派一个唯一的 ID(从 1 开始),生产者可以将这些序列号与消息关联起来,以便跟踪消息的状态;
  • 当消息被投递到所有匹配的队列之后,RabbitMQ 会异步地发送一个确认(ACK,包含消息的唯一 ID)给生产者,表明消息已送达;
  • 如果 RabbitMQ 因自身内部错误导致消息丢失,就会发送一条 nack(Basic.Nack) 命令,生产者同样可以在回调方法中处理该 nack(如消息重发);
  • 如果消息和队列是可持久化的,那么确认消息会在将消息写入磁盘之后才会发出。
  • 适用场景:对数据安全性要求较高的场景,如金融交易、订单处理。

    三种确认策略

    ① 单独确认(Publishing Messages Individually)

    每发送一条消息后就同步等待确认,收到确认(或超时/收到 nack 抛出异常)后才发送下一条。这实际上是一种串行同步等待的方式,尤其对持久化消息还需要等待写盘(fsync),速度非常慢——实测发布 200 条消息耗时约 6000+ ms。优点是实现简单,但吞吐量很低(每秒只有几百条)。

    ② 批量确认(Publishing Messages in Batches)

    每发送一批消息(如 100 条)后再统一等待这批消息的确认,发送下一批前先等上一批全部确认完毕,最后收尾确认剩余消息。吞吐量相比单独确认能提升 20~30 倍——实测发布 200 条消息耗时约 130 ms。

    缺点:出现 nack 或超时时,不清楚具体哪条消息出了问题,客户端需要把这一整批消息全部重发,会带来明显的重复消息。当消息经常丢失时,批量确认的性能反而会下降。

    ③ 异步确认(Handling Publisher Confirms Asynchronously)

    编程实现最复杂,但效率最高。信道提供了 addConfirmListener 方法注册 ConfirmListener 回调接口,其中包含两个回调方法:

    • handleAck(deliveryTag, multiple):处理服务端返回的 ack;
    • handleNack(deliveryTag, multiple):处理服务端返回的 nack。

    其中 deliveryTag 是消息的序号,multiple 为 true 表示序号小于等于 deliveryTag 的所有消息都被批量确认了。

    实现思路:为每个信道维护一个已发送消息序号的有序集合——发送消息前用 getNextPublishSeqNo() 获取下一个序号并加入集合;收到 ack 回调时,multiple=false 移除当前序号,multiple=true 清除集合中小于等于该序号的所有元素;收到 nack 回调时按业务做消息重发。这样生产者不阻塞、不间断地发送消息,确认由回调异步处理,实测发布 200 条消息仅需约 73 ms。

    💡 官方还提醒:不要在确认回调中直接重发 nack 的消息——回调运行在客户端库的 I/O 线程中,不宜在该线程执行信道操作;更好的做法是把待重发的消息放入内存队列,由发送线程轮询处理。

    三种策略性能对比(实测)
    消息数量单独确认批量确认异步确认
    200 条 6931 ms 137 ms 73 ms
    500 条 15805 ms 246 ms 107 ms

    💡 结论:单独确认最慢(串行同步);批量确认大幅提速但故障时需整批重发;异步确认最快,且消息数越多优势越明显,还能精确定位丢失的消息——生产环境推荐使用异步确认。


    六、七种模式对比总结

    模式交换机类型核心特点适用场景
    Simple 简单模式 内置交换机 一生产一消费,点对点 消息只能被单个消费者处理
    Work Queue 工作队列 内置交换机 多消费者竞争,消息不重复 集群环境做异步处理、任务分配
    Publish/Subscribe 发布订阅 Fanout 一条消息复制多份广播,所有绑定队列都收到 实时通知、广播消息(如气象数据分发)
    Routing 路由模式 Direct RoutingKey 与 BindingKey 完全一致才路由 按规则精确分发(如日志分级)
    Topics 通配符模式 Topic * 匹配一个单词,# 匹配 0~N 个单词 灵活匹配和过滤消息
    RPC 远程调用 内置交换机 双队列 + replyTo + correlationId 实现回调 需要拿到远程处理结果的场景
    Publisher Confirms 发布确认 无关 信道 confirm 模式 + 异步 ACK/NACK 数据安全性要求高(金融、订单)

    一句话记忆:前两种模式(Simple、Work Queue)解决"消费能力"问题;中间三种(Publish/Subscribe、Routing、Topics)围绕 Exchange 的类型解决"消息路由"问题;最后两种(RPC、Publisher Confirms)解决"可靠性与交互"问题。

    如果这篇文章对你有帮助,欢迎点赞、收藏、评论支持!有问题也欢迎在评论区交流~ 🐰


    推荐阅读

    • RabbitMQ 官网:https://www.rabbitmq.com
    • 官方教程:RabbitMQ Tutorials
    • Spring AMQP 文档:Spring AMQP
    赞(0)
    未经允许不得转载:171主机测评 » RabbitMQ篇章-从核心概念到七大工作模式认识RabbitMQ
    分享到: 更多 (0)

    评论 抢沙发

    • 昵称 (必填)
    • 邮箱 (必填)
    • 网址