RabbitMQ 面试题

4 阅读20分钟

RabbitMQ 面试题及答案汇总

基础概念篇

1. 什么场景适合使用 RabbitMQ?

答案:

RabbitMQ 适用于以下场景:

场景类型具体应用
异步处理用户注册后发送邮件、短信通知等异步任务,避免同步阻塞
应用解耦订单系统与库存系统分离,通过消息队列通信,降低系统耦合度
流量削峰秒杀活动、抢购场景,消息队列缓存请求,平滑处理高峰流量
日志处理分布式系统日志收集、分析,与主业务分离
消息推送实时消息推送、站内通知等

2. RabbitMQ 的默认端口是多少?

答案:

RabbitMQ 默认使用以下端口:

端口用途
5672AMQP 协议端口,用于客户端连接
15672Management 管理界面端口(需启用 management 插件)
25672集群通信端口
1883MQTT 协议端口(如果启用)
61613STOMP 协议端口(如果启用)

3. RabbitMQ 的体系结构都有哪些内容?

答案:

RabbitMQ 的核心架构包括以下组件:

┌─────────────────────────────────────────────────────────────┐
│                        Producer                              │
│                    (消息生产者)                               │
└─────────────────────────┬───────────────────────────────────┘
                          │
                          ▼
┌─────────────────────────────────────────────────────────────┐
│                    Exchange(交换机)                         │
│     ┌─────────┐ ┌─────────┐ ┌─────────┐ ┌─────────┐        │
│     │ Direct  │ │ Fanout  │ │ Topic   │ │ Headers │        │
│     └─────────┘ └─────────┘ └─────────┘ └─────────┘        │
└─────────────────────────┬───────────────────────────────────┘
                          │
          ┌───────────────┼───────────────┐
          ▼               ▼               ▼
    ┌──────────┐    ┌──────────┐    ┌──────────┐
    │ Queue 1  │    │ Queue 2  │    │ Queue 3  │
    └────┬─────┘    └────┬─────┘    └────┬─────┘
         │               │               │
         ▼               ▼               ▼
┌─────────────────────────────────────────────────────────────┐
│                       Consumer                              │
│                     (消息消费者)                            │
└─────────────────────────────────────────────────────────────┘
组件说明
Producer消息生产者,负责发送消息到交换机
Exchange交换机,接收生产者消息,根据规则转发到队列
Binding绑定关系,定义交换机与队列之间的路由规则
Queue队列,存储消息的容器
Consumer消息消费者,从队列中获取并处理消息
ConnectionTCP 连接,客户端与 RabbitMQ 之间的物理连接
Channel通道,基于 Connection 的虚拟连接,提高并发效率

项目实战篇

4. 项目中:RabbitMQ 在你项目哪里用到了?

参考答案:

根据实际项目经验,常见应用场景:

场景示例:
├── 用户模块
│   └── 用户注册 → 发送邮件验证码、发送注册成功短信
├── 订单模块
│   └── 订单支付成功 → 发送消息 → 库存系统扣减库存
│                  → 发送消息 → 物流系统创建配送单
│                  → 发送消息 → 积分系统增加积分
├── 消息通知模块
│   └── 系统通知 → 站内消息、App推送、邮件通知
└── 日志模块
    └── 业务日志 → 异步写入日志系统

回答要点:

  • 结合自己项目的实际场景描述
  • 说明为什么选择 MQ 而非同步调用
  • 体现项目的复杂度和业务规模

5. 项目中:RabbitMQ 的使用步骤?

答案:

Spring Boot 集成 RabbitMQ 标准步骤:

// 1. 添加 Maven 依赖
<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-amqp</artifactId>
</dependency>

// 2. 配置文件 application.yml
rabbitmq:
  host: localhost
  port: 5672
  username: guest
  password: guest
  virtual-host: /

// 3. 配置类 - 定义交换机、队列、绑定关系
@Configuration
public class RabbitMQConfig {
    
    @Bean
    public DirectExchange orderExchange() {
        return new DirectExchange("order.exchange");
    }
    
    @Bean
    public Queue orderQueue() {
        return new Queue("order.queue", true);  // durable=true 持久化
    }
    
    @Bean
    public Binding orderBinding() {
        return BindingBuilder
            .bind(orderQueue())
            .to(orderExchange())
            .with("order.create");
    }
}

// 4. 生产者 - 发送消息
@Autowired
private RabbitTemplate rabbitTemplate;

public void sendOrderMessage(Order order) {
    rabbitTemplate.convertAndSend(
        "order.exchange",    // 交换机
        "order.create",      // 路由键
        order                // 消息内容
    );
}

// 5. 消费者 - 接收消息
@RabbitListener(queues = "order.queue")
public void handleOrderMessage(Order order) {
    // 业务处理
    log.info("收到订单消息: {}", order);
}

6. RabbitMQ 有几种工作模式?

答案:

RabbitMQ 共有 6 种工作模式:

模式说明典型场景
1. 简单模式(Simple)一个生产者 → 一个队列 → 一个消费者单任务处理
2. 工作队列模式(Work Queue)一个生产者 → 多个消费者竞争消费任务分发、负载均衡
3. 发布/订阅模式(Pub/Sub)一个生产者 → 多个队列(Fanout)广播通知
4. 路由模式(Routing)根据路由键选择性发送到队列(Direct)定向消息
5. 主题模式(Topic)支持通配符匹配路由键(Topic)灵活的消息路由
6. 头信息模式(Headers)根据消息头属性匹配(Headers)复杂路由规则

7. RabbitMQ 的交换机有几种类型?

答案:

交换机有 4 种类型:

类型路由规则示例路由键
Direct完全匹配路由键order.create 只匹配 order.create
Fanout广播到所有绑定队列(忽略路由键)任何路由键都发送到所有队列
Topic支持通配符匹配order.* 匹配 order.create
order.# 匹配 order.create.send
Headers根据消息头属性匹配根据 x-match: all/any 判断

代码示例:

// Direct 交换机
DirectExchange directExchange = new DirectExchange("direct.exchange");

// Fanout 交换机
FanoutExchange fanoutExchange = new FanoutExchange("fanout.exchange");

// Topic 交换机
TopicExchange topicExchange = new TopicExchange("topic.exchange");

// Headers 交换机
HeadersExchange headersExchange = new HeadersExchange("headers.exchange");

8. RabbitMQ 如何通过 Java API 代码创建交换机、队列和绑定关系?

答案:

// 获取连接和通道
Connection connection = connectionFactory.newConnection();
Channel channel = connection.createChannel();

// 1. 声明交换机
channel.exchangeDeclare(
    "my.exchange",    // 交换机名称
    "direct",         // 交换机类型
    true,             // durable: 持久化
    false,            // autoDelete: 不自动删除
    null              // arguments: 扩展参数
);

// 2. 声明队列
channel.queueDeclare(
    "my.queue",       // 队列名称
    true,             // durable: 持久化
    false,            // exclusive: 非独占
    false,            // autoDelete: 不自动删除
    null              // arguments: 扩展参数(如 TTL、死信队列等)
);

// 3. 建立绑定关系
channel.queueBind(
    "my.queue",       // 队列名称
    "my.exchange",    // 交换机名称
    "my.routing.key"  // 路由键
);

// 4. 发送消息
channel.basicPublish(
    "my.exchange",
    "my.routing.key",
    MessageProperties.PERSISTENT_TEXT_PLAIN,
    "Hello RabbitMQ".getBytes()
);

// 5. 消费消息
channel.basicConsume(
    "my.queue",
    true,              // autoAck: 自动确认
    "my.consumer",     // 消费者标签
    false,             // noLocal
    false,             // noAck
    null,              // 扩展参数
    deliverCallback    // 消息回调
);

9. RabbitMQ 的 TTL 是什么意思?

答案:

TTL(Time To Live) = 消息存活时间

设置位置说明
队列 TTL队列中所有消息的默认过期时间
消息 TTL单条消息的过期时间(优先级高于队列 TTL)

代码示例:

// 队列级别 TTL(所有消息统一过期时间)
Map<String, Object> args = new HashMap<>();
args.put("x-message-ttl", 10000);  // 10秒
Queue queue = new Queue("ttl.queue", true, false, false, args);

// 消息级别 TTL
AMQP.BasicProperties properties = new AMQP.BasicProperties.Builder()
    .expiration("5000")  // 5秒
    .build();
channel.basicPublish(exchange, routingKey, properties, message.getBytes());

应用场景:

  • 订单超时未支付自动取消
  • 验证码过期
  • 临时缓存数据

10. 项目中:你的这个功能,用到的工作模式是哪一种?

参考答案:

根据实际项目描述,常见回答:

示例回答:

"我们项目使用的是【工作队列模式 + 主题模式】的组合"

场景描述:
- 订单支付成功后,需要同时通知:库存服务、物流服务、积分服务
- 我们创建了 topic.exchange 交换机
- 绑定三个队列:
  - stock.#    → 库存队列(处理库存扣减)
  - logistics.# → 物流队列(处理配送创建)
  - points.#   → 积分队列(处理积分增加)
  
- 发送消息时:
  - 库存相关消息路由键:stock.order.deduct
  - 物流相关消息路由键:logistics.order.create
  - 积分相关消息路由键:points.order.add

11. 项目中:你的这个功能,如果是主题模式,那么路由 KEY 是如何设计的?

参考答案:

路由键设计规范:

格式:{业务模块}.{操作类型}.{具体动作}

示例:
├── order.create          订单创建
├── order.pay.success      订单支付成功
├── order.pay.failed       订单支付失败
├── order.cancel           订单取消
├── stock.deduct           库存扣减
├── stock.return           库存退回
├── logistics.create       物流创建
├── logistics.update       物流更新
└── user.register         用户注册

设计原则:

  1. 层次分明,用 . 分隔
  2. 前面是业务模块,后面是具体操作
  3. 便于使用通配符批量匹配

12. 项目中:你的这个功能,生产者服务是哪个,负责做什么?

参考答案:

示例:

生产者服务:订单服务(order-service)

职责:
1. 监听订单状态变化
2. 构建消息内容(订单信息、用户信息、业务参数)
3. 选择合适的交换机和路由键发送消息
4. 记录消息发送日志
5. 处理消息发送失败的重试逻辑

代码示例:
@Service
public class OrderProducer {
    
    @Autowired
    private RabbitTemplate rabbitTemplate;
    
    public void sendOrderCreatedEvent(Order order) {
        Map<String, Object> message = new HashMap<>();
        message.put("orderId", order.getId());
        message.put("userId", order.getUserId());
        message.put("amount", order.getAmount());
        message.put("timestamp", System.currentTimeMillis());
        
        rabbitTemplate.convertAndSend(
            "order.topic",
            "order.create",
            message
        );
    }
}

13. 项目中:你的这个功能,消息内容是什么?

参考答案:

消息内容设计:

{
    "msgId": "uuid-xxx-xxx",          // 消息唯一标识(用于幂等校验)
    "businessType": "ORDER_CREATED",  // 业务类型
    "timestamp": 1712345678900,       // 消息发送时间
    "data": {
        "orderId": "ORD202404150001",
        "userId": "U10001",
        "orderAmount": 299.00,
        "items": [
            {"productId": "P001", "quantity": 2},
            {"productId": "P002", "quantity": 1}
        ],
        "receiver": {
            "name": "张三",
            "phone": "13800138000",
            "address": "江西省吉安市..."
        }
    },
    "retryCount": 0                    // 重试次数
}

设计要点:

  • 必须包含 msgId 用于消息追踪和幂等校验
  • 包含足够业务信息,消费者无需再查询数据库
  • 设置时间戳,便于问题排查

14. 项目中:你的这个功能,消费者服务是哪个,负责做什么?

参考答案:

示例:

消费者服务:库存服务(stock-service)

职责:
1. 监听库存相关队列
2. 接收并解析消息内容
3. 执行库存扣减业务逻辑
4. 处理异常情况(库存不足等)
5. 手动确认消息消费成功

代码示例:
@Component
@Slf4j
public class StockConsumer {
    
    @RabbitListener(queues = "stock.queue")
    public void handleStockMessage(Map<String, Object> message, 
                                   Channel channel, 
                                   @Header(AmqpHeaders.DELIVERY_TAG) long deliveryTag) {
        try {
            // 1. 解析消息
            String msgId = (String) message.get("msgId");
            String businessType = (String) message.get("businessType");
            Map<String, Object> data = (Map<String, Object>) message.get("data");
            
            // 2. 幂等性校验(检查是否已处理过)
            if (stockService.isProcessed(msgId)) {
                log.info("消息已处理,跳过: {}", msgId);
                channel.basicAck(deliveryTag, false);
                return;
            }
            
            // 3. 执行业务逻辑
            stockService.deductStock(data);
            
            // 4. 标记为已处理
            stockService.markAsProcessed(msgId);
            
            // 5. 确认消息
            channel.basicAck(deliveryTag, false);
            
        } catch (Exception e) {
            log.error("处理消息异常", e);
            // 拒绝消息, requeue=false 进入死信队列
            channel.basicNack(deliveryTag, false, false);
        }
    }
}

消息可靠性篇

15. RabbitMQ 的消息可靠性如何保证?

答案:

6 步保障消息可靠传递:

生产者 ──→ 交换机 ──→ 队列 ──→ 消费者
   │                      │        │
   │ ①Confirm 确认        │ ②持久化 │ ⑤手动 ACK
   │                      │        │
   └──────────────────────┴────────┘
            全链路可靠保障
环节保障措施
① 生产者确认开启 publisher-confirms,消息发送后等待 Broker 确认
② 消息持久化消息 persistent=true + 队列 durable=true
③ 交换机持久化交换机 durable=true
④ 事务机制(可选)channel.txSelect() 开启事务,性能损耗大
⑤ 消费者确认autoAck=false,手动 basicAck 确认成功消费
⑥ 消息补偿定期扫描未确认消息,进行重发或告警

配置示例:

spring:
  rabbitmq:
    # 生产者确认
    publisher-confirm-type: correlated
    publisher-returns: true
    # 消费者手动 ACK
    listener:
      simple:
        acknowledge-mode: manual

16. RabbitMQ 多线程消费消息如何保证消息有序性?

答案:

问题分析:

多线程/多消费者消费时,消息可能乱序,原因:

  • 线程执行速度不同
  • 网络延迟差异
  • 业务处理时长不一致

解决方案:

方案说明适用场景
方案1:单线程消费禁用并发,concurrency=1性能要求不高
方案2:按订单ID分桶相同订单消息路由到同一队列订单相关操作
方案3:分区(Shard)按用户/订单ID哈希分区到不同队列高并发场景
方案4:Redis 分布式锁消费前加锁,处理完解锁需要严格顺序

代码示例 - 按订单分桶:

// 使用一致性哈希,相同订单ID路由到同一队列
@Component
public class OrderMessageRouter {
    
    public String routeMessage(String orderId) {
        // 根据订单ID计算路由键
        int bucket = Math.abs(orderId.hashCode()) % 10;
        return "order.queue." + bucket;
    }
}

17. RabbitMQ 重复消费怎么处理?

答案:

核心思路:消费者实现幂等性

                    ┌─────────────┐
                    │  消息队列    │
                    └──────┬──────┘
                           │ 重复发送
                           ▼
┌─────────────────────────────────────────────┐
│              消费者幂等性处理                  │
│  ┌─────────┐  ┌─────────┐  ┌─────────┐      │
│  │ 方案1   │  │ 方案2   │  │ 方案3   │      │
│  │ 数据库  │  │ Redis   │  │ 消息ID  │      │
│  │ 唯一索引│  │ 去重    │  │ 状态机  │      │
│  └─────────┘  └─────────┘  └─────────┘      │
└─────────────────────────────────────────────┘

方案1:数据库唯一索引

// 消息ID作为唯一索引,重复插入会报错
@Table(name = "msg_process_log")
public class MsgProcessLog {
    @Id
    private String msgId;  // 唯一索引
    private String status;
    private LocalDateTime createTime;
}

// 消费时
try {
    msgProcessLogMapper.insert(log);
    // 执行业务
} catch (DuplicateKeyException e) {
    // 已处理过,直接返回
}

方案2:Redis 去重

public boolean isDuplicated(String msgId) {
    String key = "msg:processed:" + msgId;
    // SETNX + 过期时间
    Boolean result = redisTemplate.opsForValue()
        .setIfAbsent(key, "1", 24, TimeUnit.HOURS);
    return !result;  // false 表示是新消息
}

方案3:业务状态机

// 订单状态只能正向流转:待支付 → 已支付 → 已完成
// 重复消息(已完成的订单再次收到支付成功消息)直接忽略
if ("已支付".equals(order.getStatus()) && "ORDER_PAID".equals(eventType)) {
    return;  // 状态已到达,跳过
}

18. RabbitMQ 的消息幂等性如何保证?

答案:

幂等性保障的 5 种方案:

方案实现方式优缺点
1. 数据库唯一约束msgId 唯一索引简单可靠,需数据库支持
2. Redis 去重SETNX + 过期时间性能好,需依赖 Redis
3. 业务状态判断状态机流转校验无额外存储,依赖业务规则
4. 分布式锁Redis/ZK 锁性能损耗,适合强一致性场景
5. 消息表记录消息处理状态完整可靠,增加复杂度

最佳实践:

@RabbitListener(queues = "order.queue")
public void processMessage(OrderMessage message, Channel channel) {
    String msgId = message.getMsgId();
    
    // 1. 幂等性校验
    if (idempotentService.isProcessed(msgId)) {
        log.info("消息已处理,跳过: {}", msgId);
        channel.basicAck(message.getDeliveryTag(), false);
        return;
    }
    
    try {
        // 2. 业务处理
        doBusiness(message);
        
        // 3. 标记已处理
        idempotentService.markProcessed(msgId);
        
        // 4. 确认消息
        channel.basicAck(message.getDeliveryTag(), false);
        
    } catch (Exception e) {
        // 5. 失败处理
        channel.basicNack(message.getDeliveryTag(), false, false);
    }
}

19. RabbitMQ 的延迟消息实现方案有几种?

答案:

3 种主流延迟消息实现方案:

方案原理优缺点
方案1:TTL + 死信队列消息过期后进入死信队列简单,但资源浪费,不支持任意延迟
方案2:延迟插件rabbitmq_delayed_message_exchange 插件支持任意延迟,需安装插件
方案3:定时轮询数据库/Redis 存储,定时扫描简单准确,但有延迟,适合低并发

方案1:TTL + 死信队列

┌──────────────┐    TTL过期     ┌──────────────┐
│  delay.queue  │ ───────────→  │  dead.queue  │
│  (设置TTL)   │               │  (真正消费)  │
└──────────────┘               └──────────────┘
// 配置延迟队列
@Bean
public Queue delayQueue() {
    Map<String, Object> args = new HashMap<>();
    args.put("x-dead-letter-exchange", "");        // 死信交换机
    args.put("x-dead-letter-routing-key", "dead.queue");  // 死信路由键
    args.put("x-message-ttl", 10000);              // 10秒延迟
    return new Queue("delay.queue", true, false, false, args);
}

方案2:延迟插件

// 声明延迟交换机(需安装 rabbitmq_delayed_message_exchange 插件)
@Bean
public CustomExchange delayedExchange() {
    Map<String, Object> args = new HashMap<>();
    return new CustomExchange("delayed.exchange", "x-delayed-message", true, false, args);
}

// 发送延迟消息
rabbitTemplate.convertAndSend(
    "delayed.exchange",
    "routing.key",
    message,
    msg -> {
        msg.getMessageProperties().setDelay(30000);  // 30秒延迟
        return msg;
    }
);

20. RabbitMQ 的消息积压如何处理?

答案:

消息积压原因分析:

消费速度 < 生产速度

┌────────┐    快速生产    ┌────────┐    慢速消费    ┌────────┐
│ Producer│ ──────────→ │ Queue  │ ──────────→ │Consumer│
└────────┘              └────────┘              └────────┘
                       【积压中...】

解决方案:

措施说明
1. 增加消费者提升并行消费能力
2. 增加 prefetchprefetchCount 提高批量获取数量
3. 批量消费一次拉取多条,减少 ACK 次数
4. 异步处理消费逻辑异步化,快速返回
5. 消费者限流生产端限流,源头控制
6. 扩容增加消费者节点

代码示例 - 增加消费者和预取:

spring:
  rabbitmq:
    listener:
      simple:
        # 增加并发消费者数量
        concurrency: 10
        max-concurrency: 50
        # 每次预取消息数量
        prefetch: 250
        # 手动 ACK
        acknowledge-mode: manual

紧急处理流程:

# 1. 查看积压情况
rabbitmqctl list_queues name messages messages_ready messages_unacked

# 2. 临时新增消费者(紧急扩容)
# 3. 开启消息转移(将积压消息转移到新队列,快速消费)
# 4. 修复消费者问题后恢复

21. RabbitMQ 有几种确认机制?

答案:

4 种确认机制:

机制说明配置可靠性
1. 自动 ACK消息投递到消费者即确认autoAck=true⚠️ 低,可能丢消息
2. 手动 ACK消费者处理完成后手动确认autoAck=false✅ 高,推荐使用
3. 生产者 ConfirmBroker 收到消息后确认publisher-confirm✅ 高
4. 生产者 Return消息无法路由时返回publisher-returns✅ 高

手动 ACK 的 3 种方式:

// 1. 确认单条消息
channel.basicAck(deliveryTag, false);

// 2. 拒绝单条消息(可选择是否重新入队)
channel.basicNack(deliveryTag, false, true);   // requeue=true 重新入队
channel.basicNack(deliveryTag, false, false);  // requeue=false 进入死信队列

// 3. 批量确认(小于等于当前 deliveryTag 的所有消息)
channel.basicAck(deliveryTag, true);

22. RabbitMQ 手动确认 callback 有几种?

答案:

3 种确认回调:

回调类型触发时机使用场景
ConfirmCallbackBroker 确认收到消息生产者确认消息已到达 Broker
ReturnCallback消息无法路由到队列处理无法投递的消息
AcknowledgeCallback消费者确认消息记录消费状态、审计

代码示例:

@Configuration
public class RabbitMQConfig {
    
    @Autowired
    private RabbitTemplate rabbitTemplate;
    
    @PostConstruct
    public void init() {
        // 1. ConfirmCallback - 确认消息到达 Broker
        rabbitTemplate.setConfirmCallback((correlationData, ack, cause) -> {
            if (ack) {
                log.info("消息成功到达Broker: {}", correlationData.getId());
            } else {
                log.error("消息未到达Broker: {},原因: {}", 
                    correlationData.getId(), cause);
                // 记录失败,进行重发
            }
        });
        
        // 2. ReturnCallback - 消息无法路由到队列
        rabbitTemplate.setReturnsCallback(returned -> {
            log.error("消息无法路由到队列: {}, exchange: {}, routingKey: {}",
                returned.getMessage(), returned.getExchange(), 
                returned.getRoutingKey());
            // 记录或告警
        });
    }
}

// 3. 消费者 AcknowledgeCallback
@RabbitListener(queues = "my.queue")
public void handle(Message message, Channel channel, 
                   @Header(AmqpHeaders.DELIVERY_TAG) long deliveryTag) {
    try {
        // 业务处理...
        
        // 手动确认
        channel.basicAck(deliveryTag, false);
        
    } catch (Exception e) {
        // 拒绝并进入死信队列
        channel.basicNack(deliveryTag, false, false);
    }
}

23. RabbitMQ 用于生产消息的 Java API 类叫什么?一般用什么方法生产消息?

答案:

核心类:RabbitTemplate

RabbitTemplate 是 Spring AMQP 提供的消息发送模板类,封装了复杂的 API 调用。

常用发送方法:

方法说明示例
convertAndSend()自动序列化对象发送rabbitTemplate.convertAndSend("ex", "key", obj)
convertAndSend(exchange, routingKey, message)指定交换机和路由键完整参数指定
convertAndSend(routingKey, message, messagePostProcessor)发送前处理消息设置延迟、优先级等
execute(ChannelCallback)执行原生 Channel 操作高级用法

完整示例:

@Service
public class OrderMessageProducer {
    
    @Autowired
    private RabbitTemplate rabbitTemplate;
    
    // 方式1:直接发送对象(自动序列化)
    public void sendOrder(Order order) {
        rabbitTemplate.convertAndSend(
            "order.exchange",
            "order.create",
            order
        );
    }
    
    // 方式2:发送带延迟的消息
    public void sendDelayMessage(Order order, long delayMillis) {
        rabbitTemplate.convertAndSend(
            "delay.exchange",
            "order.delay",
            order,
            message -> {
                message.getMessageProperties().setDelay((int) delayMillis);
                return message;
            }
        );
    }
    
    // 方式3:发送带消息属性的消息
    public void sendWithProperties(Order order) {
        rabbitTemplate.convertAndSend(
            "order.exchange",
            "order.create",
            order,
            message -> {
                message.getMessageProperties().setPriority(10);
                message.getMessageProperties().setCorrelationId(UUID.randomUUID().toString());
                message.getMessageProperties().setExpiration("60000");  // 60秒过期
                return message;
            }
        );
    }
}

24. RabbitMQ 的消费者一般都添加什么注解?注解里有什么属性?

答案:

核心注解:@RabbitListener

@RabbitListener(
    queues = "queue-name",           // 队列名(必填)
    queuesToDeclare = @Queue(...),    // 声明并监听队列
    exchanges = @Exchange(...),      // 声明并绑定交换机
    bindings = @QueueBinding(...),   // 完整的绑定配置
    concurrency = "3-10",            // 并发消费者数量(最小-最大)
    ackMode = "MANUAL",              // 确认模式
    prefetch = "10",                 // 预取数量
    priority = "10"                  // 消费者优先级
)

完整示例:

// 方式1:简单配置
@RabbitListener(queues = "order.queue")
public void handleOrder(Order order) {
    // 处理消息
}

// 方式2:完整配置(推荐)
@RabbitListener(
    queuesToDeclare = @Queue(
        name = "order.queue",
        durable = "true",
        arguments = @Argument(name = "x-dead-letter-exchange", value = "dlx.exchange")
    ),
    bindings = @QueueBinding(
        value = @Queue(name = "order.queue"),
        exchange = @Exchange(name = "order.exchange", type = ExchangeTypes.TOPIC),
        key = "order.#"
    ),
    concurrency = "5",
    ackMode = "MANUAL",
    prefetch = "20"
)
public void handleOrder(Message message, Channel channel) {
    try {
        // 业务处理
        channel.basicAck(message.getMessageProperties().getDeliveryTag(), false);
    } catch (Exception e) {
        channel.basicNack(message.getMessageProperties().getDeliveryTag(), false, false);
    }
}

常用属性汇总:

属性类型说明
queuesString[]监听的队列名称
queuesToDeclare@Queue声明队列(可指定 TTL、死信等)
bindings@QueueBinding绑定关系(交换机、队列、路由键)
concurrencyString并发数,如 "3" 或 "3-10"
ackModeAckMode确认模式:MANUAL/AUTO/NONE
prefetchString预取消息数量
priorityString消费者优先级

25. 什么时候会出现死信?

答案:

死信(Dead Letter)产生的 3 种场景:

场景触发条件说明
1. 消息被拒绝basicNack / basicRejectrequeue=false消费者明确拒绝消息
2. 消息过期超过 TTL 未被消费队列或消息级别的过期时间
3. 队列溢出队列达到最大长度,最早的消息被丢弃x-max-length 限制

触发条件代码示例:

// 配置死信队列
@Bean
public Queue deadLetterQueue() {
    return new Queue("dlq.order", true);
}

@Bean
public DirectExchange deadLetterExchange() {
    return new DirectExchange("dlx.order");
}

@Bean
public Binding deadLetterBinding() {
    return BindingBuilder
        .bind(deadLetterQueue())
        .to(deadLetterExchange())
        .with("dead.order");
}

// 普通队列配置死信规则
@Bean
public Queue orderQueue() {
    Map<String, Object> args = new HashMap<>();
    args.put("x-dead-letter-exchange", "dlx.order");
    args.put("x-dead-letter-routing-key", "dead.order");
    // 队列最大长度(可选)
    args.put("x-max-length", 10000);
    // 消息过期时间(可选)
    args.put("x-message-ttl", 60000);
    
    return new Queue("order.queue", true, false, false, args);
}

26. RabbitMQ 中死信如何产生的?

答案:

死信产生的完整流程:

                    ┌─────────────────────────────────────────┐
                    │              普通队列                    │
                    │  order.queue (绑定死信交换机)            │
                    └──────────────────┬──────────────────────┘
                                       │
        ┌──────────────────────────────┼──────────────────────────────┐
        │                              │                              │
        ▼                              ▼                              ▼
   ┌─────────┐                   ┌─────────┐                    ┌─────────┐
   │消息被拒绝│                   │消息过期 │                    │队列溢出 │
   │requeue  │                   │TTL结束  │                    │超出长度 │
   │  =false │                   │         │                    │         │
   └────┬────┘                   └────┬────┘                    └────┬────┘
        │                             │                              │
        │         触发死信             │                              │
        │    ┌────────────┐           │                              │
        └──→ │ DLX交换机  │ ←──────────┴──────────────────────────┘
             │dlx.exchange│
             └─────┬──────┘
                   │
                   ▼
             ┌───────────┐
             │ 死信队列   │
             │ dlq.order  │
             └───────────┘

代码模拟死信产生:

@RabbitListener(queues = "order.queue")
public void handle(Message message, Channel channel) throws IOException {
    long deliveryTag = message.getMessageProperties().getDeliveryTag();
    
    try {
        Order order = JSON.parseObject(message.getBody(), Order.class);
        
        // 业务逻辑校验
        if (orderService.validate(order)) {
            orderService.process(order);
            channel.basicAck(deliveryTag, false);  // ✅ 正常确认
        } else {
            // 业务校验失败,拒绝消息,进入死信队列
            channel.basicNack(deliveryTag, false, false);  // ❌ requeue=false
        }
        
    } catch (Exception e) {
        // 系统异常,记录日志后拒绝
        log.error("处理异常: {}", e.getMessage());
        channel.basicNack(deliveryTag, false, false);
    }
}

27. 过期时间是在队列中还是在消息中设置?

答案:

两种方式都可以,优先级:消息 TTL > 队列 TTL

设置位置配置项作用范围粒度
队列级别x-message-ttl队列中所有消息粗粒度,统一过期时间
消息级别expiration单条消息细粒度,可设置不同过期时间

代码示例:

// 方式1:队列级别 TTL(所有消息统一过期时间)
Map<String, Object> args = new HashMap<>();
args.put("x-message-ttl", 60000);  // 60秒
Queue queue = new Queue("order.queue", true, false, false, args);

// 方式2:消息级别 TTL(每条消息可不同)
AMQP.BasicProperties properties = new AMQP.BasicProperties.Builder()
    .expiration("30000")  // 30秒
    .build();
channel.basicPublish(exchange, routingKey, properties, message.getBytes());

// 方式3:Spring AMQP 简化写法
rabbitTemplate.convertAndSend(exchange, routingKey, message, m -> {
    m.getMessageProperties().setExpiration("30000");
    return m;
});

重要特性:

⚠️ 注意:消息的 TTL 如果设为 0,表示消息永不过期(除非队列设置了 TTL)


28. RabbitMQ 的消息生产完没消费到是什么原因造成的?

答案:

排查链路:

Producer → Exchange → Binding → Queue → Consumer
   │          │         │        │        │
   ▼          ▼         ▼        ▼        ▼
 ①网络/ACK  ②交换机   ③绑定    ④队列    ⑤消费者
            类型错误    错误     满/删    问题
环节可能原因排查方法
① 生产者问题网络中断、未开启 Confirm、未开启 Returns检查日志、确认回调是否收到
② 交换机问题交换机不存在、类型错误、无权限确认交换机声明、类型是否匹配
③ 绑定问题路由键不匹配、无绑定关系检查 Binding 配置
④ 队列问题队列不存在、队列被删除、队列溢出检查队列状态、最大长度设置
⑤ 消费者问题消费者异常退出、消费慢、抛出异常检查消费者日志、并发配置

排查命令:

# 1. 查看交换机
rabbitmqctl list_exchanges

# 2. 查看队列
rabbitmqctl list_queues name messages messages_ready

# 3. 查看绑定关系
rabbitmqctl list_bindings

# 4. 查看消费者
rabbitmqctl list_consumers

# 5. 查看连接
rabbitmqctl list_connections

29. 除了 RabbitMQ 你还用过其他 MQ 吗?区别在哪里?

答案:

三大主流 MQ 对比:

特性RabbitMQKafkaRocketMQ
公司/社区VMware/PivotalApacheAlibaba
吞吐量万级百万级十万级
延迟微秒级毫秒级毫秒级
消息可靠性⭐⭐⭐ 高⭐⭐ 中⭐⭐⭐ 高
功能丰富度⭐⭐⭐⭐ 丰富⭐⭐ 基础⭐⭐⭐⭐ 丰富
分布式事务不支持不支持支持
延迟消息支持(需插件)不支持原生支持
顺序消息支持(需队列)支持(分区)支持(队列)
官方维护活跃非常活跃活跃
适用场景企业级、复杂业务大数据、日志、流处理电商、金融交易

选择建议:

业务复杂度高、功能要求多  → RabbitMQ / RocketMQ
大数据量、日志采集       → Kafka
金融级、高并发交易       → RocketMQ
简单解耦、异步任务       → RabbitMQ

30. RabbitMQ 搭建集群了吗?你用的什么集群?

答案:

集群架构类型:

类型说明适用场景
普通集群队列分散在多个节点,队列内容一致提高可用性
镜像集群队列内容复制到所有节点高可靠、数据同步
Federation 插件跨机房/跨地域联邦多数据中心
Shovel 插件消息从一个节点移动到另一个数据迁移

镜像集群配置示例:

# rabbitmq.conf 配置
# 定义镜像策略
ha-mode = all
ha-sync-mode = automatic
ha-promote-on-shutdown = always
# 命令行配置镜像策略
rabbitmqctl set_policy ha-all "^ha\." '{"ha-mode":"all","ha-sync-mode":"automatic"}' --apply-to queues

Docker Compose 快速搭建集群:

# docker-compose.yml
version: '3'
services:
  rabbitmq-1:
    image: rabbitmq:3-management
    environment:
      RABBITMQ_ERLANG_COOKIE: cluster_cookie
    ports:
      - "5672:5672"
      - "15672:15672"
  
  rabbitmq-2:
    image: rabbitmq:3-management
    environment:
      RABBITMQ_ERLANG_COOKIE: cluster_cookie
    ports:
      - "5673:5672"
    links:
      - rabbitmq-1
  
  rabbitmq-3:
    image: rabbitmq:3-management
    environment:
      RABBITMQ_ERLANG_COOKIE: cluster_cookie
    ports:
      - "5674:5672"
    links:
      - rabbitmq-1

附录:核心概念速查表

概念说明
AMQPAdvanced Message Queuing Protocol,高级消息队列协议
Exchange交换机,接收生产者消息并转发到队列
Queue队列,存储消息的容器
Binding绑定关系,连接交换机和队列的桥梁
Routing Key路由键,决定消息发送到哪个队列
Consumer消费者,从队列中获取消息处理
TTLTime To Live,消息存活时间
Dead Letter死信,无法正常消费的消息
ACKAcknowledge,确认消息已处理
Prefetch预取数量,消费者一次性获取的消息数
Virtual Host虚拟主机,隔离不同环境/租户的消息