# 07.面试八股-RabbitMQ

# 🔥 话题一:MQ 作用(异步、解耦、削峰)

# 🟢 第一问

面试官:为什么要在系统里引入消息队列?说出三个典型场景。

💡 答案要点
作用 说明 举例
异步 非核心逻辑不阻塞主流程 订单创建后发送短信/邮件
解耦 生产者不关心谁消费 订单完成后,库存/积分/物流各自订阅
削峰 缓冲突发流量,保护下游 秒杀场景,下单请求先入队,消费者慢慢处理

# 🟡 第二问(追问)

面试官:异步一定能提升性能吗?有没有副作用?

不一定,异步不是提升单任务执行速度,而是通过非阻塞提升系统吞吐量;CPU 密集型、简单短任务用异步反而会增加线程切换和调度开销,降低性能。

  • 入队出队延迟变高了

  • 链路变复杂了,排查问题更难

  • 上下文开销变大

# 🔥 话题二:消息丢失(重点中的重点)

# 🟢 第一问

面试官:消息从生产到消费,在哪些环节可能丢失?怎么防止?

三个阶段都可能丢

阶段 丢消息原因 解决方案
生产 → MQ 网络抖动,发送方以为成功但 MQ 没收到 生产者确认机制(publisher confirm)
MQ 存储 MQ 宕机,内存中的消息丢了 持久化 + 主从/集群
MQ → 消费者 消费者拿到消息还没处理完就自动 ACK 手动 ACK,处理完再确认

# 🟡 第二问(追问)

面试官:RabbitMQ 怎么开启生产者确认?和事务的区别是什么?

只需在 Channel 上调用 confirmSelect() 方法即可。

  1. 普通 Confirm 模式(单条同步等待):每发送一条消息,就调用 waitForConfirms() 方法等待服务端的确认。这种方式是同步阻塞的,性能提升有限。
  2. 批量 Confirm 模式:发送一批消息后,调用一次 waitForConfirms() 进行确认。如果返回失败,需要重发这一整批消息。
  3. 异步 Confirm 模式(生产环境最推荐):提供一个回调函数(ConfirmListener),生产者发送消息后无需阻塞等待,继续发送下一条。当 Broker 成功处理或处理失败时,会异步回调通知生产者。

“RabbitMQ 开启生产者确认只需要在 Channel 上调用 confirmSelect() 方法。在实际生产中,我们通常会使用异步 Confirm 模式,通过注册回调监听器(ConfirmListener)来接收 Broker 返回的 ACK 或 NACK,这样既能保证消息可靠投递,又不会阻塞生产者的发送线程。

关于它和事务的区别,核心在于性能。RabbitMQ 的事务机制是同步阻塞的,发一条消息就要等待一次事务提交响应,这会导致吞吐量急剧下降,完全无法满足高并发的生产需求。而 Publisher Confirm 机制是异步非阻塞的,它允许生产者连续发送消息,通过回调来确认消息状态,吞吐量可以达到事务机制的几十倍甚至上百倍。所以,除非是极低频且对强一致性有严苛要求的特殊场景,否则在生产环境中我们都会选择 Publisher Confirm 来替代事务机制,来保障消息的可靠投递。”

# 🔴 第三问(深度追问)

面试官:消费者手动 ACK 时,如果处理消息过程中宕机了,消息会丢吗?会重复吗?

不会丢失,因为没有确认的情况下,消息队列是不会删除消息的

可能会重复,消息如果没被确认,会返回到队列中,对消息进行二次消费

解决方案:

可以加幂等性判断,在每次消息处理之前,判断根据全局ID判断此条消息是否被处理过,如果处理过直接ACK,未处理就按逻辑进行处理

# 🔥 话题三:消息重复(幂等性)

# 🟢 第一问

面试官:除了消费者宕机,还有哪些场景会导致消息重复?

  • MQ确认超时,消息处理慢,MQ没接到ACK,重发

  • 生产者发送消息后的确认超时,让生产者以为没收到,重新发送

  • 批量确认消息失败,部分成功的消息,重新发送

# 🟡 第二问(追问)

面试官:有哪些实现幂等的方法?你的项目里怎么做的?

  • 利用数据库主键或唯一索引(Unique Key)的天然特性。

  • 为每条消息分配一个全局唯一的 ID(如 msgId 或业务单号)。消费者在处理前,先去 Redis 执行 SETNX(或 setIfAbsent)操作。

  • 在更新核心业务状态时,在 SQL 的 WHERE 条件中带上当前的前置状态。例如更新订单状态:UPDATE orders SET status = 'PAID' WHERE order_id = 'xxx' AND status = 'UNPAID'

# 🔥 话题四:消息顺序

# 🟢 第一问

面试官:RabbitMQ 怎么保证消息的顺序性?

RabbitMQ 保证顺序的核心依然是局部有序’
最基础的方案是
‘单队列 + 单消费者,利用队列天然的 FIFO 特性来保证顺序。
但在高并发场景下,我们会采用分区队列(分片)的策略。比如根据订单 ID 进行哈希,将同一笔订单的所有消息路由到同一个队列,并为每个队列分配独立的单线程消费者。这样既保证了同一订单内的严格有序,又通过多队列并行提高了系统的整体吞吐量。

此外,在使用 RabbitMQ 时我会特别注意消息重试导致的乱序问题。因为 RabbitMQ 失败重试的消息会被扔到队列尾部,极易打乱顺序。所以对于保序消息,我会关闭自动重试入队,配合死信队列来做异常兜底;同时在生产端开启 Publisher Confirm 机制,确保消息按序到达 Broker。

# 🔥 话题五:Nacos(服务注册与发现)

# 🟢 第一问

面试官:服务注册到 Nacos 后,消费者怎么知道调用哪个实例?

  • 服务启动,注册自己的服务信息到Nacos

  • 服务从Nacos注册中心拉取服务列表

  • 通过负载均衡选择一个实例来调用

# 🟡 第二问

面试官:Nacos 的临时实例和永久实例有什么区别?

  • 健康检测机制不同,Nacos临时实例中,客户端主动上报心跳,如果超时则被自动剔除,在永久实例中,服务端主动探测,如果实例宕机,会标记为不健康,不会剔除

  • 存储方式不同,临时实例存储在内存中,每次重启都重新加载,永久实例在磁盘中,重启后依然存在

临时实例适合普通微服务业务

永久实例适合Mysql,Redis等长期稳定的基本业务

# 🔥 话题六:OpenFeign

# 🟢 第一问

面试官:Feign 的原理是什么?怎么实现远程调用像本地调用一样?

在应用启动阶段,Feign会自动扫描带有@FeignClient的注解,自动将他们创建为代理对象注册到Spring容器中

在实际应用阶段,当我们调用接口方法时,代理对象会拦截这次请求,解析请求中的@GetMapping之类的注解和参数相关注解,动态构建出一个HTTP请求,结合路径和负载均衡发送到真实的服务中,调用,返回json,并解析为java对象

# 🟡 第二问(追问)

面试官:Feign 调用超时怎么配置?为什么要配?

feign:
  client:
    config:
      default:
        connectTimeout: 5000   # 连接超时
        readTimeout: 5000      # 读取超时
1
2
3
4
5
6
  • 原生 Feign(脱离 Spring Cloud 单独使用):
    • 连接超时(Connect Timeout):默认 10秒
    • 读取超时(Read Timeout):默认 60秒 (实现快速失败,防止占用CPU)
  • Spring Cloud 环境下的 Feign(实际项目中最常见):
    • 由于 Feign 集成了 Ribbon(或 LoadBalancer)作为负载均衡组件,Ribbon 的默认配置会覆盖 Feign 的原生配置
    • 连接超时(Connect Timeout):默认 1秒
    • 读取超时(Read Timeout):默认 1秒(适配一部分慢接口)

# 🔥 话题七:Gateway(路由、限流、降级)

# 🟢 第一问

面试官:Gateway 的核心组成是什么?一个请求进来是怎么处理的?

Router(路由):id+uri+predicate(断言:匹配条件,如:路径/请求头)+filter

filter(过滤器):请求前/后处理

请求 ==> 网关 Handler Mapping (根据路径匹配Router) ==> FilteringWebHandler(加载并调用路由下的过滤器链) ==> 执行过滤器链 ==> 转发到相应的服务

# 🟡 第二问(追问)

面试官:Gateway 怎么实现限流?

Gateway 内置限流(基于 Redis + 令牌桶):

spring:
  cloud:
    gateway:
      routes:
        - id: order-service
          uri: lb://order-service
          filters:
            - name: RequestRateLimiter
              args:
                redis-rate-limiter.replenishRate: 10  # 每秒令牌数
                redis-rate-limiter.burstCapacity: 20 # 最大令牌数
1
2
3
4
5
6
7
8
9
10
11

# 🟢 第三问

面试官:服务熔断和降级的区别?

熔断更像一种自我保护机制,他关注的是自我稳定性。当下游服务故障时,熔断器会自动断开,防止因为故障发生雪崩,将服务设置为半开半关状态可自动恢复

降级更像是一种兜底策略,关注用户体验,当系统流量过大时,主动关闭非核心功能,比如评论、推荐,优先保全核心功能支付下单

在实际项目中,通常配合使用,触发熔断后设置一个降级策略兜底。

最近更新: 9/19/2026, 1:27:08 PM
编程NOTE   |