RabbitMQ 面试题及答案汇总
基础概念篇
1. 什么场景适合使用 RabbitMQ?
答案:
RabbitMQ 适用于以下场景:
| 场景类型 | 具体应用 |
|---|---|
| 异步处理 | 用户注册后发送邮件、短信通知等异步任务,避免同步阻塞 |
| 应用解耦 | 订单系统与库存系统分离,通过消息队列通信,降低系统耦合度 |
| 流量削峰 | 秒杀活动、抢购场景,消息队列缓存请求,平滑处理高峰流量 |
| 日志处理 | 分布式系统日志收集、分析,与主业务分离 |
| 消息推送 | 实时消息推送、站内通知等 |
2. RabbitMQ 的默认端口是多少?
答案:
RabbitMQ 默认使用以下端口:
| 端口 | 用途 |
|---|---|
| 5672 | AMQP 协议端口,用于客户端连接 |
| 15672 | Management 管理界面端口(需启用 management 插件) |
| 25672 | 集群通信端口 |
| 1883 | MQTT 协议端口(如果启用) |
| 61613 | STOMP 协议端口(如果启用) |
3. RabbitMQ 的体系结构都有哪些内容?
答案:
RabbitMQ 的核心架构包括以下组件:
┌─────────────────────────────────────────────────────────────┐
│ Producer │
│ (消息生产者) │
└─────────────────────────┬───────────────────────────────────┘
│
▼
┌─────────────────────────────────────────────────────────────┐
│ Exchange(交换机) │
│ ┌─────────┐ ┌─────────┐ ┌─────────┐ ┌─────────┐ │
│ │ Direct │ │ Fanout │ │ Topic │ │ Headers │ │
│ └─────────┘ └─────────┘ └─────────┘ └─────────┘ │
└─────────────────────────┬───────────────────────────────────┘
│
┌───────────────┼───────────────┐
▼ ▼ ▼
┌──────────┐ ┌──────────┐ ┌──────────┐
│ Queue 1 │ │ Queue 2 │ │ Queue 3 │
└────┬─────┘ └────┬─────┘ └────┬─────┘
│ │ │
▼ ▼ ▼
┌─────────────────────────────────────────────────────────────┐
│ Consumer │
│ (消息消费者) │
└─────────────────────────────────────────────────────────────┘
| 组件 | 说明 |
|---|---|
| Producer | 消息生产者,负责发送消息到交换机 |
| Exchange | 交换机,接收生产者消息,根据规则转发到队列 |
| Binding | 绑定关系,定义交换机与队列之间的路由规则 |
| Queue | 队列,存储消息的容器 |
| Consumer | 消息消费者,从队列中获取并处理消息 |
| Connection | TCP 连接,客户端与 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.createorder.# 匹配 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 用户注册
设计原则:
- 层次分明,用
.分隔 - 前面是业务模块,后面是具体操作
- 便于使用通配符批量匹配
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. 增加 prefetch | prefetchCount 提高批量获取数量 |
| 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. 生产者 Confirm | Broker 收到消息后确认 | 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 种确认回调:
| 回调类型 | 触发时机 | 使用场景 |
|---|---|---|
| ConfirmCallback | Broker 确认收到消息 | 生产者确认消息已到达 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);
}
}
常用属性汇总:
| 属性 | 类型 | 说明 |
|---|---|---|
queues | String[] | 监听的队列名称 |
queuesToDeclare | @Queue | 声明队列(可指定 TTL、死信等) |
bindings | @QueueBinding | 绑定关系(交换机、队列、路由键) |
concurrency | String | 并发数,如 "3" 或 "3-10" |
ackMode | AckMode | 确认模式:MANUAL/AUTO/NONE |
prefetch | String | 预取消息数量 |
priority | String | 消费者优先级 |
25. 什么时候会出现死信?
答案:
死信(Dead Letter)产生的 3 种场景:
| 场景 | 触发条件 | 说明 |
|---|---|---|
| 1. 消息被拒绝 | basicNack / basicReject 且 requeue=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 对比:
| 特性 | RabbitMQ | Kafka | RocketMQ |
|---|---|---|---|
| 公司/社区 | VMware/Pivotal | Apache | Alibaba |
| 吞吐量 | 万级 | 百万级 | 十万级 |
| 延迟 | 微秒级 | 毫秒级 | 毫秒级 |
| 消息可靠性 | ⭐⭐⭐ 高 | ⭐⭐ 中 | ⭐⭐⭐ 高 |
| 功能丰富度 | ⭐⭐⭐⭐ 丰富 | ⭐⭐ 基础 | ⭐⭐⭐⭐ 丰富 |
| 分布式事务 | 不支持 | 不支持 | 支持 |
| 延迟消息 | 支持(需插件) | 不支持 | 原生支持 |
| 顺序消息 | 支持(需队列) | 支持(分区) | 支持(队列) |
| 官方维护 | 活跃 | 非常活跃 | 活跃 |
| 适用场景 | 企业级、复杂业务 | 大数据、日志、流处理 | 电商、金融交易 |
选择建议:
业务复杂度高、功能要求多 → 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
附录:核心概念速查表
| 概念 | 说明 |
|---|---|
| AMQP | Advanced Message Queuing Protocol,高级消息队列协议 |
| Exchange | 交换机,接收生产者消息并转发到队列 |
| Queue | 队列,存储消息的容器 |
| Binding | 绑定关系,连接交换机和队列的桥梁 |
| Routing Key | 路由键,决定消息发送到哪个队列 |
| Consumer | 消费者,从队列中获取消息处理 |
| TTL | Time To Live,消息存活时间 |
| Dead Letter | 死信,无法正常消费的消息 |
| ACK | Acknowledge,确认消息已处理 |
| Prefetch | 预取数量,消费者一次性获取的消息数 |
| Virtual Host | 虚拟主机,隔离不同环境/租户的消息 |