RabbitMQ-第五章:RabbitMQ的面试题
目录1.什么是RabbitMQ2.RabbitMQ可以做什么3.在RabbitMQ中怎么保证消息的顺序问题。就比如我有ABC三个消息。我必须要按照ABC进行消费4.RabbitMQ怎么保证不会重复消费5.RabbitMQ 怎么保证消息不会被丢失6.在RabbitMQ 中我们要不要对全部的消息进行持久化为什么1.什么是RabbitMQRabbitMQ是一个开源的消息队列系统RabbitMQ使用AMQP高级消息队列协议标准。而RabbitMQ主要的目的是提供可靠的消息传递。确保消息的可靠性和顺序性。也提供了比较灵活的路由交换机策略和保证消息消费确认机制。RabbitMQ是基于AMQP协议工作也支持些其他协议。它通过消息的发布与订阅模式来实现消息的传递与消费。RabbitMQ主要包含的角色生产者交换机队列和消费者。生产者负责生成消息而交换机负责接收生产者的消息并根据具体的规则路由传递到队列。而队列负责存储这些消息。让消费者进行消费。交换机主要分广播交换机头部交换机强制匹配主题交换机和模糊匹配主题交换机四种交换机类型但是现在头部交换机因为效率原因导致基本上都不使用了。2.RabbitMQ可以做什么RabbitMQ 的本质是 “消息中转站”解决分布式系统中 “通信” 的核心问题核心能力有 3 个解耦系统间不直接调用通过消息传递数据比如订单系统不用直接调用库存系统只需发送 “扣减库存” 消息库存系统监听消息处理后续改库存逻辑不影响订单系统在APS系统中锁单库存需要通知订购易系统没有直接去调用订购易系统就是通过mq去通知。也不影响其他业务。异步无需等待接口返回比如用户下单后不用等 “发短信、减库存、生成物流单” 都完成才返回只需发送消息用户立即收到 “下单成功”后续逻辑异步处理削峰填谷高并发场景下比如秒杀大量请求先进入 RabbitMQ 队列消费端按能力慢慢处理避免直接打垮数据库。为什么要用RabbitMQ简单说如果你的场景是中小并发、需要灵活路由比如按不同条件转发消息、有延迟任务需求选 RabbitMQ如果是高吞吐日志 / 大数据场景选 Kafka如果是国产生态 分布式事务选 RocketMQ。3.在RabbitMQ中怎么保证消息的顺序问题。就比如我有ABC三个消息。我必须要按照ABC进行消费想在 RabbitMQ 中保证 A、B、C 消息严格按顺序消费核心是让消息在同一队列中按序入队 单消费者消费多消费者会打乱顺序这也是 RabbitMQ 保证消息顺序的核心逻辑先明确为什么消息会乱序RabbitMQ本身是采用队列先进先出策略天然保证消息的顺序性。但是以下场景会导致乱序1.多个消费者消费同一个队列就比如A到队列后被消费者1取走B到队列后被消费者2取走如果消费者2处理更快导致B会优先于A完成。2.消息重试或者死信如果A处理失败重新进入队列BC就先被消费。除非引用Redis在代码手动确认A是否被消费如果消费了就执行B,否则就重新进入队列。3.交换机路由到多队列A,B,C被路由到不同队列消费顺序不可控。保证消息顺序的核心方案1.使用RabbitMQ保证消息顺序性核心原则「单队列 单消费者」队列是 RabbitMQ 唯一能保证 FIFO 的组件只要 A、B、C 按顺序进入同一个队列且只有一个消费者消费这个队列就能严格按 A→B→C 顺序处理。但是生产环境很少会出现这样的情况。基本上都是进阶场景需要高并发 保顺序单消费者处理速度慢我们可按 “业务分组” 拆分队列而非多消费者消费同一队列比如按用户 ID 哈希用户 1 的 A、B、C 到队列 1用户 2 的 A、B、C 到队列 2每个队列对应一个消费者既保证单队列顺序又提升整体并发。如果使用RabbitMQ队列保证消息顺序性就需要避免以下操作禁止开启自动确认auto_ackTrue自动确认会导致消费者同时取走多条消息处理快的先完成打乱顺序消息失败不重新入队若 A 处理失败重新入队会到队列尾部B、C 已被消费顺序乱了→ 失败消息丢入死信队列后续人工处理而非重新入队避免延迟队列乱序若用延迟队列同一批次的延迟消息要到同一个延迟队列且消费队列单消费者生产者必须按序发送生产者若异步发送比如多线程发 A、B、C消息入队顺序可能乱→ 生产者单线程按序发送或加锁保证发送顺序。2.通过标识唯一标识A,B,C代码判断是否执行如果没有执行就重新入队列。保证消息顺序性。使用RabbitMQ队列保证消息的顺序性有一定的局限性需要注意的事项很多。所以我们可以使用代码来实现定制化的消息顺序性。1.当生产者生产消息就给消息进行标记例如给 A/B/C 消息添加「阶段标识」stage1/2/3和「全局唯一 ID」比如 task_id1001表示这是同一批任务的第 1/2/3 步2.将状态存储到Redis或者存储到数据库。就比如生产者发送了消息可以根据全局唯一ID存储一条记录设置状态为1。如果是A执行的时候查询状态为1就执行其他BC查询为1就查询进入队列。A执行成功了触发手动确认机制ACK机制进行修改状态。3.消费者进行消费消费者 A只处理 stage1 的消息执行 A 代码完成后更新状态为 “stage1_done”消费者 B只处理 stage2 的消息但消费前先查状态只有 stage1_done 才执行 B 代码否则重新入队等待消费者 C只处理 stage3 的消息消费前查状态只有 stage2_done 才执行 C 代码否则重新入队等待队列拆分可选优化 —— 拆分为 3 个队列queue_a/queue_b/queue_cA 消息发 queue_a、B 发 queue_b、C 发 queue_c每个队列对应一个消费者通过状态依赖控制执行顺序。这个方案既保留了多消费者的灵活性每个消费者专注自己的代码又通过状态依赖保证了整体顺序是分布式系统中 “多消费者 有序执行” 的通用方案。4.RabbitMQ怎么保证不会重复消费我们没有办法保证彻底杜绝重复消费但是可以通过“ACK确认机制”“幂等性设计”让重复消费无业务影响。1.先明确一点RabbitMQ重复消费无法100%杜绝的因为RabbitMQ重复消费的根源是网络不可靠或者系统宕机导致 1). 消费者处理业务但未发ACK,RabbitMQ重推。2). 生产者为收到RabbitMQ的发送确认重发消息。3). 网络延迟导致消息的重试。而我们的目标不是“不让消息重复”而是“重复消费也不影响业务结果”。2.RabbitMQ保证不会重复消费的核心解决方案是1.通过合理的配置ACK确认机制从源头降低重复消费的可能性 关闭自动ACK确认机制严格手动确认ACK。手动确认ACK最好是业务逻辑执行完成幂等标记完成之后才手动调用ACK. 2.业务层面实现幂等性在每条消息添加全局唯一标识消费端先标识是否已处理如果处理过就跳过这样就解决重复消费问题。业务层面实现幂等性可以通过redis或者通过数据库存储幂等性标识。如果使用redis 存储幂等性标识需要注意redis 的过期时间过期时间合理Redis 标记的过期时间要长于消息最大重试周期如消息最多重试 24 小时过期设 1 天5.RabbitMQ 怎么保证消息不会被丢失RabbitMQ消息丢失的核心场景有三个生产者发送消息后消息未成功抵达 RabbitMQ 服务器 生产者丢失RabbitMQ 服务器接收到消息但自身宕机 / 重启导致消息丢失服务器丢失消费者接收到消息但处理过程中宕机消息既没处理完也没确认。消费者丢失1,生产者层面确保消息成功投递到RabbitMQ我们的核心策略是发布确认机制失败重试本地兜底策略。避免消息发送出去就失联了。我们会开启发布确认机制RabbitMQ会在消息成功写入队列后给生产者返回确认而生产者仅收到确认成功后才判定发送成功。如果失败就触发三次重试机制。再次失败会将失败状态缓存到数据库由人工确认。我们的RabbitMQ 在生产者发送消息之前会将这个消息的消息体交换机路由等就存储到数据库。如果生产者返回确认失败了触发重试机制也失败了。将这条数据的状态修改为失败。由人工在页面确认失败原因并手动触发重新投递到RabbitMQ2.RabbitMQ服务器层面确保消息持久化存储我们的核心策略是交换机队列消息三重持久化避免服务器丢失消息。交换机持久化声明时设置durable true队列持久化声明时设置durable true消息持久化发送时设置deliveryMode 2Spring AMQP 默认持久化。RabbitMQ交换机队列和消息三重持久化会导致性能损耗加剧存储压力增大运维成本加大。1.性能明显下降会导致写入延迟。非持久化写入级别达到毫秒级别但是持久化写入级别会提升5-10倍写入时间的速度。在高并发会更明显。2.TPS吞吐量下降官方表明持久化与非持久化的TPS吞吐量达到15。非持久化TPS 可以达到10万TPS, 持久化可以达到2万TPS持久化机制我们要根据平衡可靠性和性能来优化方案核心业务订单、支付开启全持久化 镜像队列接受性能损耗非核心业务日志、通知关闭持久化用内存队列优先保证性能折中方案仅开启队列 / 交换机持久化消息不持久化适合允许少量丢失的场景。3.消费者层面就确保消息处理完成之后再手动触发消息确认机制我们的核心策略就是手动触发ACK消息确认机制死信队列兜底。 给业务队列配置死信队列失败消息转发到死信队列后续人工排查。 在消费者端我们也会有一张表存储发送的消息的状态和消息的内容等。状态有刚到消费者0 和消费成功1 和消费失败2.如果失败了会存储消息失败原因。而且这张表也实现了幂等性的功能。保证了消息重复消费不会影响业务功能。当重复消费查询到数据就不会执行业务代码。6.在RabbitMQ 中我们要不要对全部的消息进行持久化为什么在RabbitMQ中我们绝对不要对全部的消息进行持久化因为我们对全部的消息进行持久化了就会导致性能和TPS吞吐量下降5-10倍的速度。磁盘资源和运维资源增大我们具体要不要对消息的持久化就需要根据消息的业务重要级别进行持久化。我们可以将消息分成核心重要普通级别。只针对核心消息进行三重持久化。重要级别可以持久化交换机和队列。普通级别可以不进行持久化。7.在RabbitMQ 怎么处理消息的堆积问题如果对方问这个问题时首先得和对方确认消息堆积是因为什么原因导致堆积的1.短时间内突发大量消息比如秒杀、活动生产速度远大于消费速度2. 消费者挂了、消费速度慢、消费逻辑耗时久、并发数不够生产紧急处理1.如果是生产上出现这个问题我们可以通过临时扩容消费者复杂现有的消费者代码快速启动多台/多个实例提高消费并发数。让消费速度大于生产速度。2.如果是可以接受延迟情况的消息队列可以将堆积的消息转发到临时队列先消费核心消息后续慢慢处理临时队列 RabbitMQ Web 控制台进行转发到临时队列长期确认但是我们创建这个队列时会综合考虑这个队列的生产会发送多少消息需要多少消费者进行消费才不会导致消息堆积问题1.如果我们这个队列生产者消息会有很多我们就会将这个队列进行拆分成多个小队列每个队列都对应一个消费者进行分解消息队列的消息堆积压力2.我们会在消息队列进行监控预警 提前发现堆积避免问题扩大 监控队列的消息堆积数、消费速率、消费者数量 当堆积数超过阈值比如 1000 条触发告警邮件、短信如果超过阈值就紧急处理1.RabbitMQ 是什么它有哪些主要应用场景2.RabbitMQ的基本架构是什么包括哪些核心组件3.如何在RabbitMQ中声明一个队列有哪些必须的参数4.什么是RabbitMQ中的生产者和消费者5.RabbitMQ的交换机有哪几种类型他们订单工作方式是什么RabbitMQ使用场景1.广播模式SAP系统推送物料基础信息是通过RabbitMQ的广播交换机推送的绑定的全部队列上面。2.