ARTICLE DETAIL

资讯详情

深耕郑州网站建设与运营推广的一线实战洞察。

RabbitMQ从入门到实战:核心原理、代码实操与面试八股全解析

RabbitMQ从入门到实战:核心原理、代码实操与面试八股全解析 “自学八股”这个标题我太熟了前阵子啃RabbitMQ的时候就是这么过来的。网上讲RabbitMQ的文章一搜一大把但大部分要么是官方文档翻译腔要么是知识点零散得能当拼图玩。这篇东西我酝酿了很久想把它写成一份“从安装到面试都能用”的笔记既讲清楚工作原理也把实际编码里的坑和面试爱问的八股点串起来。不管你是刚接触消息队列的新手还是用了段时间想系统捋一遍基础知识点的开发者这篇文章应该都能给你点实在的东西。先说清楚RabbitMQ是什么它是一个基于AMQP协议的开源消息队列中间件核心作用是在不同系统之间传递消息让生产者和消费者解耦。你可以把它理解成一个快递中转站——生产者把包裹丢进去就不用管了消费者按自己的节奏去取两边完全不用等对方。这种异步、削峰、解耦的能力几乎是现在分布式系统里离不开的基础设施。整篇文章我会按照“为什么用、怎么装、核心概念、工作原理、代码实操、面试考点、问题排查”这条线来写尽量做到每一段都有实际价值不是那种看完就忘的泛泛而谈。1. 先搞清楚RabbitMQ到底解决了什么问题1.1 没有消息队列的时候系统是怎么“堵车”的很多同学刚开始学RabbitMQ都有一个困惑我在代码里直接调用对方接口不就行了为什么要中间塞一个消息队列这个困惑很正常因为单体应用时代确实不需要MQ。但系统一旦拆成多个服务问题就来了。举个最典型的例子用户下单后系统要执行扣库存、发短信、送积分、更新推荐位。如果这些操作全部同步调用下单接口的响应时间就是这些操作耗时的总和。短信服务万一超时了用户那边转圈转半天订单其实已经创建成功了体验特别糟糕。更麻烦的是大促期间订单量暴涨这些下游服务根本扛不住瞬时的流量冲击一个服务挂了整个下单链路全瘫。引入RabbitMQ之后下单服务只需要把“订单已创建”这个消息丢进队列然后立刻返回“下单成功”。扣库存、发短信这些服务各自去队列里拉消息处理处理快慢互不影响。就算某个服务挂了消息还会留在队列里等它恢复之后继续消费。这就是消息队列最核心的价值异步解耦加流量削峰。1.2 RabbitMQ的核心价值与典型场景消息队列家族里Kafka、RocketMQ、RabbitMQ各有各的主场。RabbitMQ最擅长的是复杂路由和灵活的消息投递策略它不像Kafka那样追求海量吞吐和日志处理而是更强调消息的可靠性、多语言支持和细粒度的路由控制。实际业务中我梳理过几个典型的适合用RabbitMQ的场景异步处理用户注册后发送欢迎邮件、短信通知不需要同步等结果。应用解耦订单系统和库存系统之间通过消息通信订单服务不需要依赖库存服务的接口稳定性。流量削峰秒杀、抢券场景下先把请求写入消息队列后端服务按自己能承受的速度消费避免数据库被瞬间打爆。数据同步多个业务系统之间同步用户信息、商品信息通过广播式交换机fanout一次投递给所有订阅方。1.3 为什么是RabbitMQ而不是别的MQ这个问题面试基本都会被问到我也对比过几种主流MQ的差异。Kafka的设计目标是大吞吐量日志采集它的优势是顺序写磁盘和分区消费但它的消息确认机制相对来说没有RabbitMQ那么精细RocketMQ是阿里开源的消息队列事务消息和延时消息很强但在社区生态和跨语言支持上不如RabbitMQ丰富。RabbitMQ基于Erlang开发天生自带高并发、低延迟的特性管理界面功能齐全各种语言客户端SDK特别成熟。对我们做业务开发的人来说如果项目中需要一个稳定、易上手、支持复杂路由的中间件RabbitMQ几乎是最稳妥的选择。后面讲的代码演示你就会发现它的API极其直观。2. 环境准备Windows与Docker两种安装方式全记录2.1 Windows安装与启动RabbitMQ是Erlang写的所以Windows下第一步是先装Erlang再装RabbitMQ顺序不能反。这里有个常见的坑Erlang和RabbitMQ之间有版本对应关系最好去RabbitMQ官网的版本兼容列表里确认一下。我当初图省事下了个最新版Erlang结果RabbitMQ启动直接报错查了半天才发现是版本不兼容。安装步骤很简单两步走去Erlang官网下载对应版本的OTP安装包一路Next就行。去RabbitMQ官网下载Windows安装包安装完成后用管理员身份打开命令行执行cd C:\Program Files\RabbitMQ Server\rabbitmq_server-版本号\sbin rabbitmq-plugins enable rabbitmq_management这步是启用Web管理插件。默认情况下RabbitMQ只开放AMQP协议的5672端口不启用管理插件的话你只能靠命令行操作特别难受。启用之后浏览器访问http://localhost:15672用默认账号guest/guest登录。注意guest账号默认只能在localhost访问远程访问需要单独建账号这个后面会讲到。2.2 Docker安装与启动如果你用Windows开发我更推荐直接用Docker跑RabbitMQ省去本地环境一大堆依赖问题。一条命令就能起一个带管理界面的实例docker run -d --name rabbitmq \ -p 5672:5672 \ -p 15672:15672 \ -e RABBITMQ_DEFAULT_USERadmin \ -e RABBITMQ_DEFAULT_PASSadmin123 \ rabbitmq:3-management这里有几个点想提醒一下镜像一定要带management标签否则没有Web管理界面。rabbitmq:3-management自带管理插件省得进容器手动开。端口映射要同时映射5672AMQP协议端口客户端连接用和15672管理界面端口。RABBITMQ_DEFAULT_USER和RABBITMQ_DEFAULT_PASS是创建默认管理员账号的环境变量。用这种方式创建的账号不是guest而是你自己指定的账号远程访问没问题。Docker方式最大的好处是清理干净不想用了直接docker rm -f rabbitmq本地不留一点痕迹。我个人建议学习阶段尽量用Docker能让你的精力集中在RabbitMQ本身而不是折腾环境。2.3 安装后必做的健康检查装完之后别急着写代码先做几个简单的健康检查确认环境没问题看进程状态Windows下执行rabbitmqctl statusDocker下执行docker exec rabbitmq rabbitmqctl status。如果返回一段包含RabbitMQ版本和Erlang版本的信息说明核心服务正常。访问管理界面浏览器打开http://localhost:15672能出来登录页说明管理插件正常。检查端口监听执行netstat -an | findstr 5672Windows或ss -lntp | grep 5672Linux确认5672端口在监听。这三个检查任何一个出问题都先别往下走把环境弄好再说。实际工作中我看到太多人环境没就绪就开始写代码最后搞不清楚是环境问题还是代码问题白白浪费时间。3. 核心概念与工作原理一次搞懂十个基础名词3.1 从Producer到Consumer一条消息的完整旅程学习RabbitMQ最难迈过的一道坎就是它和普通队列不一样中间多了一层Exchange交换机。很多新手一上来就以为“把消息丢进队列”但其实RabbitMQ里消息从来不直接进队列。一条消息的完整旅程是这样的Producer生产者创建消息指定一个Exchange名字以及一个RoutingKey路由键。消息被送到RabbitMQ的Exchange上。Exchange拿着RoutingKey和当前绑定的规则做匹配决定消息应该被投递到哪个Queue队列。Queue收到消息后存储起来等待Consumer消费者来取。Consumer从Queue中拉取消息或者RabbitMQ主动推送处理完业务逻辑后发送ACK确认。用快递类比的话Producer是寄件人Exchange是快递分拣中心RoutingKey是快递单上的地址标签Queue是各个片区的配送站Consumer是快递员。寄件人不用关心快递员在哪只需要把包裹交给分拣中心分拣中心一看地址就知道丢到哪个配送站。3.2 Connection、Channel、Queue、Exchange怎么理解这几个名词是RabbitMQ最重要的基础概念很多人容易混淆我拆开一个个讲。Connection连接客户端和RabbitMQ服务器之间建立的TCP连接。这是一个重量级资源不能频繁创建和销毁。Channel信道建立在Connection之上的虚拟连接。一个TCP连接里可以同时开多个Channel每个Channel独立处理自己的消息收发。为什么需要这层设计因为如果每次收发消息都新建一个TCP连接握手开销会非常大。Channel让多个线程可以共享同一个TCP连接各走各的逻辑通道。实际编码中操作RabbitMQ大都是操作ChannelConnection创建后要留着复用。Queue队列消息的存储容器本质上是内存和磁盘里的一块数据结构。队列有名称、是否持久化、是否排他、是否自动删除等属性。RabbitMQ并不限制队列数量但每个队列都会占用一定资源所以不能无脑建队列。Exchange交换机消息的路由分发中心。它不存储消息只负责把消息往队列里路由。队列通过Binding绑定和Exchange建立关系指定自己关心哪些RoutingKey的消息。RoutingKey路由键消息的标签Exchage根据这个键和自身类型决定投递策略。Binding绑定Exchange和Queue之间的一种关联关系绑定时要指定RoutingKey。一个队列可以绑定多个Exchange一个Exchange也可以绑定多个队列。VirtualHost虚拟主机可以理解成RabbitMQ里的命名空间/租户隔离。不同VirtualHost之间的Exchange、Queue、Binding完全隔离。一个RabbitMQ实例上可以创建多个VirtualHost给不同项目或环境dev/test/prod使用。默认VirtualHost是/。这几个概念串起来就是一个完整的消息路由体系画个简图在脑子里记一下Producer → Exchange → Binding → Queue → Consumer。3.3 四种Exchange类型对比Exchange是RabbitMQ路由灵活性的核心它有四种类型面试必考。我做了一张对比表方便大家记忆类型路由规则典型场景DirectRoutingKey完全匹配点对点消息、按优先级路由Fanout忽略RoutingKey广播给所有绑定的队列全局通知、广播消息TopicRoutingKey按通配符规则匹配*匹配一个单词#匹配零个或多个单词按业务分类订阅、日志分级Headers根据消息头Headers属性匹配极少使用规则复杂Direct是最简单的交换机。生产者发送RoutingKeyorder.create队列如果绑定了order.create这个键就能收到消息。Fanout类似微信群发不管RoutingKey是什么只要队列绑定了这个交换机消息就复制一份发给它。Topic最灵活比如队列绑定的键是order.*那么order.create、order.pay都能收到消息但order.create.success收不到因为*只匹配一个单词如果绑定键是order.#那order.create.success也能收到。实际项目里用得最多的是Direct和Topic。Fanout通常做广播比如配置变更通知所有服务Topic适合按模块、按级别做动态订阅。3.4 ACK机制与消息确认消息可靠性是RabbitMQ的核心卖点这里必须说清楚ACKAcknowledgment确认应答机制。默认情况下消费者从队列取走消息后RabbitMQ会立即把这条消息从队列中删除。但如果消费者处理消息时程序崩溃了这条消息就彻底丢了。为了避免这种情况RabbitMQ提供了自动ACK和手动ACK两种模式。自动ACKautoAcktrue消费者一收到消息就自动应答RabbitMQ立即删消息。这种模式吞吐高但消息丢失风险大生产环境慎用。手动ACKautoAckfalse消费者处理完业务逻辑后主动调用basicAck告诉RabbitMQ“我处理好了你可以删了”。如果消费者在应答前崩了RabbitMQ会认为这条消息没被成功处理将其重新放回队列等下一个消费者继续处理。注意手动ACK模式下如果消费者处理消息的代码抛异常一定要调用basicNack拒绝并重新入队或者basicReject拒绝且不再入队否则消息会一直处于unacked状态越积越多。这里有个非常经典的坑忘了在finally块里做ACK导致消息一直堆积在unacked状态。我见过一次线上事故消费端代码逻辑没问题但异常分支里没有ACK也没有Nack死信队列没配消息就一直卡着消费者重启之后又重复消费了几次最后队列爆了。这个后面在排查篇里还会细说。4. 代码实操Spring Boot和C#里把RabbitMQ跑起来4.1 Spring Boot整合生产端与消费端先展示Java生态下最常见的Spring Boot整合方式。加依赖就不啰嗦了主要是spring-boot-starter-amqp。我们要做的第一件事是配置连接信息spring: rabbitmq: host: localhost port: 5672 username: admin password: admin123 virtual-host: / publisher-confirm-type: correlated publisher-returns: true注意最后两行配置publisher-confirm-type: correlated开启发送端确认publisher-returns: true开启消息无法路由时的回退回调。这是保证消息不丢的第一步后面面试篇会详细聊。发送一条消息的代码特别简单Autowired private RabbitTemplate rabbitTemplate; public void sendOrderMessage(OrderDTO order) { String json JSON.toJSONString(order); CorrelationData correlationData new CorrelationData(UUID.randomUUID().toString()); rabbitTemplate.convertAndSend( order.exchange, order.create, json, correlationData ); }convertAndSend的第一个参数是Exchange名第二个是RoutingKey第三个是消息体。RabbitTemplate默认用JDK序列化来转换对象但生产环境强烈建议手动转成JSON字符串再发避免序列化兼容性问题。接收端用RabbitListener注解很声明式Component public class OrderConsumer { RabbitListener(queues order.queue) public void onMessage(String message, Channel channel, Header(AmqpHeaders.DELIVERY_TAG) long deliveryTag) throws Exception { try { OrderDTO order JSON.parseObject(message, OrderDTO.class); // 业务处理... channel.basicAck(deliveryTag, false); } catch (Exception e) { channel.basicNack(deliveryTag, false, true); log.error(消费订单消息失败, e); } } }关键点在这里RabbitListener默认是自动ACK但如果我们手动设置ackModeMANUAL就必须在代码里自己控制ACK。上面这个示例就是手动ACK的标准写法。deliveryTag是当前消息的递增编号用于告诉RabbitMQ具体确认哪一条消息。4.2 C#环境下的RabbitMQ客户端使用除了JavaC#开发者使用RabbitMQ也相当常见。.NET环境下最常用的客户端库是RabbitMQ.ClientNuGet搜一下就能装。发送端的核心代码using RabbitMQ.Client; using System.Text; var factory new ConnectionFactory { HostName localhost, UserName admin, Password admin123, VirtualHost / }; using var connection factory.CreateConnection(); using var channel connection.CreateModel(); channel.ExchangeDeclare(order.exchange, ExchangeType.Direct, durable: true); channel.QueueDeclare(order.queue, durable: true, exclusive: false, autoDelete: false); channel.QueueBind(order.queue, order.exchange, order.create); var json JsonSerializer.Serialize(order); var body Encoding.UTF8.GetBytes(json); var properties channel.CreateBasicProperties(); properties.Persistent true; channel.BasicPublish( exchange: order.exchange, routingKey: order.create, basicProperties: properties, body: body );C#的API和Java其实是同一套模型的映射。ExchangeDeclare声明交换机QueueDeclare声明队列QueueBind建立绑定关系BasicPublish发布消息。properties.Persistent true表示消息持久化重启后消息不丢。消费端的代码var consumer new EventingBasicConsumer(channel); consumer.Received (model, ea) { var body ea.Body.ToArray(); var message Encoding.UTF8.GetString(body); try { // 业务处理 channel.BasicAck(ea.DeliveryTag, false); } catch { channel.BasicNack(ea.DeliveryTag, false, true); } }; channel.BasicConsume(queue: order.queue, autoAck: false, consumer: consumer);C#这边用事件驱动的方式处理消息BasicConsume方法里autoAck: false明确关闭自动ACK。事件回调里拿到DeliveryTag做手动确认。整体思路和Java是一样的只是语法上的差异。4.3 把JSON消息放进去的正确姿势既然热搜里有“把JSON放入RabbitMQ”这里单独展开聊聊。RabbitMQ的消息体本质上是字节数组byte[]所以任何可序列化格式都能放进去。但实践中JSON是绝对的主流原因很简单跨语言、跨平台、可读性强。Java发的消息C#能消费C#发的消息Java能消费只要两边用同一个JSON Schema。正确的JSON入队姿势把业务对象用JSON.toJSONString()Fastjson、ObjectMapper.writeValueAsString()Jackson或C#的JsonSerializer.Serialize()转成字符串。将字符串用UTF-8编码转成字节数组再发送。在消费端把字节数组用UTF-8解码再反序列化成目标对象。有个细节不要用Java默认的对象序列化ObjectOutputStream。虽然RabbitTemplate默认支持Object类型但JDK序列化出来的二进制格式只有Java能解析而且内容里有大量类路径信息体积大、不安全。一旦以后要跨语言消费就非常被动。用JSON所有语言通吃。还有一个小建议消息体里尽量带一个消息IDmessageId字段用UUID生成。这个ID可以用来做幂等判断也可以用来做全链路追踪排查问题的时候会省很多力气。5. 面试八股高频考点消息不丢失、幂等、顺序、积压5.1 消息不丢失的三段式保障“RabbitMQ怎么保证消息不丢失”这道题基本是面试必问。我整理了它的完整答案分三段第一段生产端不丢。生产者的消息可能在发送过程中丢失所以要用发送方确认机制。Spring Boot里配置publisher-confirm-type: correlated发送时带一个CorrelationData如果RabbitMQ成功收到消息并路由成功会回调acktrue如果消息无法投递比如交换机不存在会回调ackfalse。另外publisher-returns可以在消息无法路由到任何队列时触发返回值回调。这两者配合生产者就能感知消息是否成功进了队列。第二段队列中不丢。这里要开启持久化。一是队列声明时设置durabletrue这样队列本身在RabbitMQ重启后还在二是消息发布时设置MessageProperties.PERSISTENT_TEXT_PLAIN或C#里的properties.Persistent true。这样消息写入磁盘后RabbitMQ宕机重启也能恢复消息。需要注意持久化是有性能代价的如果业务对吞吐要求极高且能容忍少量丢失可以关闭持久化。第三段消费端不丢。消费者要关闭自动ACK改成手动ACK。只有业务逻辑处理成功后才调用basicAck。如果处理失败用basicNack让消息重新入队或者进入死信队列不能悄无声息地丢掉。总结一句话生产端靠Confirm队列端靠持久化消费端靠手动ACK。三层配合才能保证一条消息从发送到消费全程不丢。5.2 消费幂等性幂等性问题是消息队列的经典面试题。为什么必须关心幂等因为RabbitMQ在极端情况下会重复投递消息比如消费者处理消息后、发送ACK前网络闪断RabbitMQ没收到ACK就会把消息重新投递给另一个消费者或者消费者在手动ACK之前程序崩溃消息也会被重新投递。所以你写的消费逻辑必须天然幂等——处理两次和一次的结果一样。常见方案有三种唯一ID数据库唯一约束消息里带全局唯一ID消费时先查这个ID是否已存在存在直接ACK。或者在数据库表里加唯一索引重复插入直接报错然后吞掉。Redis标记法消费前用SETNX设置一个带过期时间的标记处理完任务后删除。如果SETNX失败说明之前已经处理过。业务状态机判断比如订单状态从“待支付”到“已支付”如果已经变成已支付再次收到支付成功消息直接忽略。我个人最推荐第一种用一个消息去重表简单直接不依赖额外的中间件。5.3 消息顺序性RabbitMQ在单队列单消费者的前提下天然保证消息顺序。但如果存在多个消费者并发消费同一个队列消息顺序就无法保证——消费者A处理第1条消息比较慢消费者B处理第2条消息比较快结果第2条先完成。面试中被问到“如何保证消息顺序”标准答法就是确保同一类需要顺序处理的消息进入同一个队列并且该队列只有一个消费者。比如订单状态变更的消息按订单ID做哈希让同一个订单的消息都路由到同一个Queue这个Queue只部署一个消费者实例或者多个实例但通过某种机制只让一个消费。也可以用一个取巧的办法消费者内部按消息ID做分区每条消息根据业务键比如订单号哈希到内存里的不同阻塞队列用多个单线程worker消费。但这么做等于自己实现了一个分区队列复杂度不低业务上如果强顺序要求尽量在架构层面收敛到单队列单消费者。5.4 消息积压怎么办线上消息积压是最让人头疼的问题之一。队列里的消息堆积成山消费速度赶不上生产速度。这里我总结下排查和解决的思路。先要分清是消费速度慢还是生产量激增。如果是消费速度慢先看消费者的日志是不是数据库慢查询、外部接口超时、或者消费线程被阻塞。如果是生产量激增大促秒杀就要考虑扩容消费者实例。快速扩容的思路是这样的如果Queue没设置x-max-priority等限制先临时增加消费者实例数量尽量让消费者并发数多于队列积压速度。注意一个队列可以被多个消费者同时消费消息会被分摊到不同消费者。如果消费者实例已经很多了还是消费不过来说明单队列的处理能力到瓶颈了。这时候可以临时建多个Queue写一个分发程序把积压的消息按规则分到多个Queue里同时启动更多消费者去消费。相当于手动做了一次分区。如果业务允许可以临时关闭手动ACK改成自动ACK吞消息但这是一种“保吞吐丢可靠”的兜底方案要衡量业务能否接受。积压问题根本上一是靠监控预防二是靠扩容快速止损。真正的性能优化消费端业务逻辑优化、数据库索引优化是事后要做的事。6. 常见问题与排查技巧实录6.1 启动失败与端口占用的排查思路新手遇到最多的问题就是RabbitMQ启动失败。Windows下启动失败的常见原因Erlang版本不兼容安装的Erlang和RabbitMQ版本对不上。打开RabbitMQ的日志文件默认在C:\Users\用户名\AppData\Roaming\RabbitMQ\log看到类似Failed to create Erlang cookie或者Unknown erlang version之类的报错基本就是版本问题。端口被占用RabbitMQ默认监听5672端口如果netstat -an | findstr 5672发现端口被别的程序占用会启动失败。常见“凶手”是别的消息中间件或者一些随机软件。直接改配置文件里的端口即可或者干掉占用端口的进程。epmd进程冲突Erlang的端口映射守护进程epmd默认监听4369端口。如果之前装过Erlang组件没卸干净可能出现epmd被其他进程占用的情况。重启电脑比手动清要省心。Docker环境下启动失败大多是端口冲突或者目录挂载权限问题。docker logs rabbitmq看一眼日志里面通常直接写了原因。6.2 Channel和Connection泄漏线上的另一大坑是连接资源泄漏。很多同学在业务代码里反复创建Connection和Channel用完不关。RabbitMQ每个连接都要占用一个TCP连接每个Channel也会占用内存和文件句柄。连接和Channel数量涨上去直到服务端口耗尽或者内存爆掉RabbitMQ就开始各种诡异的超时。我的经验是Connection是重量级资源全局只需要创建一个多个线程共享。Channel是轻量级资源但也不是无限制创建。最佳实践是每个线程一个Channel用完就close()。Spring Boot里RabbitTemplate和RabbitListener已经帮你管理好了基本不用手动操作。原生客户端使用C#或Java时手动管理Connection和Channel的代码里一定要把using或try-with-resources用起来确保异常时也能释放。6.3 性能调优的小经验最后分享几个压测和调优过程中积累的小经验批量发送如果业务允许一次批量发送多条消息比一条一条发吞吐高很多。RabbitMQ自带BasicPublish的Batch支持Spring Boot里也可以用rabbitTemplate.invoke做批量操作。消息大小控制RabbitMQ的单条消息大小默认没有硬限制但超过10MB的消息会严重影响性能。超过1MB的消息先想想能不能拆成小消息或者改成文件传输、对象存储。Prefetch Count这是消费者预取消息的数量单位是条。设置合理能大幅提升消费效率。默认情况下RabbitMQ会把队列里的消息一股脑推给消费者如果消费者处理慢就可能导致消息都堆积在消费者本地。手动ACK模式下建议设置basicQos(prefetchCount)比如channel.basicQos(10)让消费者最多同时持有10条未确认的消息。这样不会一次拉太多消息内存占用也更友好。管理界面监控RabbitMQ管理界面里能看到每个队列的消息速率、消费速率、未确认消息数。发现某个队列unacked数量一直涨赶紧看看消费者是不是出问题了。我实际压过一个系统单纯把Prefetch Count从默认值调到50消费吞吐量提升了将近3倍。这个指标非常值得花时间调。6.4 交换机绑定出问题的排查方法最后加一个实际项目里非常高频的问题消息发送成功了但消费者就是收不到。这种情况十有八九是交换机、队列、绑定的名称不匹配或者RoutingKey写错了。排查思路我总结了如下顺序打开管理界面Queues页面看消费者是否在线在线的话队列的Ready消息数是多少。如果Ready一直是0说明消息根本没进队列。去Exchanges页面点进你发送消息的交换机点Bindings标签页看绑定的队列、RoutingKey是否符合预期。如果绑定没问题再用管理界面的Publish message功能手动发一条测试消息消息体随便填RoutingKey填你代码里实际用的值看消息是否进入队列。检查代码里实际使用的Exchange名、Queue名有没有拼写错误。这种情况真的非常多尤其是复制粘贴时带上了末尾空格肉眼根本看不出来。这条排查路径能覆盖绝大多数“发到交换机但队列没收到”的场景。最后再分享一个小技巧学习RabbitMQ的最高效路径不是捧着官方文档从头啃而是先搭好环境、把最简单的消息发送和接收跑通遇到不懂的概念再回头翻文档。我之前就是死磕了好几天AMQP协议细节结果代码一行没写后来先跑通一个Demo再去理解Exchange和Binding一下就通透了。希望这篇笔记也能帮你少走点弯路有问题欢迎留言交流。
返回列表