Q1: 什么是消息队列?为什么要使用消息队列?
核心定义
消息队列(MQ)是应用程序之间的异步通信中间件,通过存储和转发消息实现系统解耦。
四大核心价值
1. 异步处理 - 提升响应速度
- 用户注册:同步300ms → 异步60ms(提升5倍)
- 非核心业务异步化(邮件、短信、积分)
2. 系统解耦 - 降低耦合度
- 订单系统不直接调用库存、积分系统
- 新增下游系统无需修改上游代码
3. 流量削峰 - 保护后端
- 秒杀:10万QPS → MQ缓冲 → 1000TPS处理
- 防止数据库被打垮
4. 数据分发 - 一对多通知
- 一条消息被多个系统消费
- 用户行为分析、推荐系统、风控系统
代价分析
- 系统复杂度↑(需处理丢失、重复、顺序)
- 一致性要求↓(强一致→最终一致)
- 运维成本↑(部署、监控、维护)
- 调试难度↑(异步链路追踪)
Q2: RabbitMQ、RocketMQ、Kafka详细对比
| 维度 | RabbitMQ | RocketMQ | Kafka |
|---|---|---|---|
| 开发语言 | Erlang | Java | Scala/Java |
| 开发公司 | Pivotal | 阿里巴巴 | Apache |
| 吞吐量 | 万级 | 10万级 | 百万级 |
| 延迟 | 微秒级 | 毫秒级 | 毫秒级 |
| 可用性 | 高(主从) | 非常高(分布式) | 非常高(分布式) |
| 事务消息 | 支持(性能差) | 原生支持 | 支持(0.11+) |
| 延迟消息 | 插件支持 | 18级别 | 不支持 |
| 消息回溯 | 不支持 | 支持 | 支持 |
| 运维难度 | 中 | 中 | 高 |
详细特性对比
1. RabbitMQ
优点:
- 消息延迟低(微秒级),适合实时性要求高的场景
- 功能完善:支持消息确认、死信队列、延迟队列、优先级队列
- 管理界面友好,易于监控
- 支持多种语言客户端
- 灵活的路由机制(Direct/Fanout/Topic/Headers Exchange)
缺点:
- 吞吐量相对较低(万级)
- Erlang语言小众,二次开发困难
- 集群模式复杂(镜像队列会影响性能)
- 消息堆积能力弱
适用场景:
- 实时通讯系统
- 任务调度系统
- 消息量不大的企业应用
- 对延迟敏感的场景
2. RocketMQ
优点:
- 吞吐量高(10万级),支持高并发
- 功能丰富:事务消息、顺序消息、延迟消息、消息过滤
- 高可用性:支持主从切换、集群部署
- 消息查询功能强大(按MessageId、Key查询)
- Java开发,易于扩展和定制
- 阿里生产环境验证
缺点:
- 社区相对Kafka小
- 需要部署NameServer
- 配置项较多,学习曲线陡
适用场景:
- 金融级应用(需要事务消息)
- 电商平台(订单、支付等核心业务)
- 需要顺序消息的场景
- 大规模分布式系统
3. Kafka
优点:
- 超高吞吐量(百万级),大数据场景首选
- 分布式设计,水平扩展能力强
- 消息持久化机制完善
- 生态系统丰富(Kafka Connect/Streams/KSQL)
- 高可用性,数据可靠性高
- 支持消息回溯
缺点:
- 不支持消息优先级
- 不原生支持延迟消息
- 单机超过64个分区性能会下降
- 依赖ZooKeeper(2.8之前)
- 运维复杂,参数调优困难
适用场景:
- 大数据实时计算
- 日志收集系统(ELK)
- 用户行为追踪
- 流式处理
- 系统监控指标收集
选型决策树
开始选型
│
├─ 消息量 > 10万/秒?
│ └─ 是 → Kafka
│
├─ 需要事务消息?
│ └─ 是 → RocketMQ
│
├─ 需要延迟消息?
│ ├─ 是 → RocketMQ 或 RabbitMQ
│ └─ 否 → 继续判断
│
├─ 消息量 < 1万/秒且追求低延迟?
│ └─ 是 → RabbitMQ
│
├─ 大数据/日志收集场景?
│ └─ 是 → Kafka
│
├─ 金融/电商等核心业务?
│ └─ 是 → RocketMQ
│
└─ 简单场景,已有Redis?
└─ 是 → Redis Stream
Q3: Producer、Consumer、Broker、Topic、Queue核心概念详解
1. Producer(生产者)
定义: 消息的产生者,负责创建并发送消息到消息队列。
职责:
- 构造消息内容
- 选择目标队列/Topic
- 处理发送失败和重试
- 确认消息发送成功
三种发送方式:
java
// 1. 同步发送(可靠性高,性能低)
SendResult result = producer.send(message);
if (result.getSendStatus() == SendStatus.SEND_OK) {
System.out.println("发送成功");
}
// 2. 异步发送(性能高,需要处理回调)
producer.send(message, new SendCallback() {
@Override
public void onSuccess(SendResult result) {
System.out.println("发送成功");
}
@Override
public void onException(Throwable e) {
System.out.println("发送失败");
}
});
// 3. 单向发送(性能最高,不关心结果)
producer.sendOneway(message);
2. Consumer(消费者)
定义: 消息的消费者,负责从消息队列中接收并处理消息。
消费模式:
- Push模式:Broker主动推送消息给消费者(RabbitMQ)
- Pull模式:消费者主动从Broker拉取消息(Kafka、RocketMQ)
消费者组(Consumer Group):
Topic: order_topic
├─ Consumer Group1: order_service (订单服务消费)
│ ├─ Consumer 1
│ └─ Consumer 2
└─ Consumer Group2: logistics_service (物流服务也消费)
├─ Consumer 1
└─ Consumer 2
特点:
- 同一个Consumer Group内的消费者共同消费一个Topic
- 每条消息只会被Group内的一个消费者消费
- 不同Group可以重复消费同一条消息
3. Broker(消息代理服务器)
定义: 消息队列的核心服务器,负责接收、存储、转发消息。
核心功能:
- 接收生产者的消息
- 存储消息(内存/磁盘)
- 投递消息给消费者
- 消息持久化
- 主从复制
- 负载均衡
RocketMQ Broker架构:
┌─────────────────────────┐
│ NameServer集群 │ (服务发现、路由)
└─────────┬───────────────┘
│
┌─────┴─────┐
│ │
┌───▼───┐ ┌───▼───┐
│Broker1│ │Broker2│
│Master │ │Master │
└───┬───┘ └───┬───┘
│ │
┌───▼───┐ ┌───▼───┐
│Broker1│ │Broker2│
│ Slave │ │ Slave │
└───────┘ └───────┘
4. Topic(主题)
定义: 消息的逻辑分类,一个Topic可以包含多个Queue/Partition。
特点:
- 发布/订阅模式
- 一条消息可以被多个消费者组消费
- 支持消息过滤
Kafka Topic结构:
Topic: order_topic
├─ Partition 0 [Leader: Broker1, Replica: Broker2, Broker3]
│ └─ Offset: 0, 1, 2, 3...
├─ Partition 1 [Leader: Broker2, Replica: Broker1, Broker3]
│ └─ Offset: 0, 1, 2, 3...
└─ Partition 2 [Leader: Broker3, Replica: Broker1, Broker2]
└─ Offset: 0, 1, 2, 3...
5. Queue(队列)
定义: 消息的存储单元,FIFO顺序。
特点:
- 点对点模式
- 每条消息只能被一个消费者消费
- 消费后消息会被删除(或标记)
Queue vs Topic对比:
Queue(点对点):
Producer → Queue → Consumer1
→ Consumer2 (竞争消费,只有一个能消费到)
→ Consumer3
Topic(发布订阅):
Producer → Topic → ConsumerGroup1 → Consumer1, Consumer2
→ ConsumerGroup2 → Consumer1, Consumer2
(每个Group都能收到消息)
Q4: 什么是消费者组(Consumer Group)?
定义
同一个Consumer Group内的消费者共同消费一个Topic,实现负载均衡。
核心特性
-
消息只被消费一次
- 一条消息只会被Group内的一个消费者消费
- 实现了消息的负载均衡
-
不同Group独立消费
- 不同Consumer Group可以重复消费同一条消息
- 实现了消息的多播
-
自动负载均衡
- 消费者动态增减时自动rebalance
- 分区/队列自动分配给消费者
示例场景
Topic: order_topic (订单创建事件)
Consumer Group1: order_service
├─ Consumer1: 消费 Partition 0, 1
└─ Consumer2: 消费 Partition 2, 3
(订单服务处理订单创建)
Consumer Group2: logistics_service
├─ Consumer1: 消费 Partition 0, 1, 2
└─ Consumer2: 消费 Partition 3
(物流服务也需要处理同样的订单事件)
Consumer Group3: data_analysis_service
└─ Consumer1: 消费所有 Partition
(数据分析服务分析订单数据)
最佳实践
-
合理设置消费者数量
- 消费者数 ≤ 分区数(超过会浪费)
- Kafka: 消费者数 = 分区数时效率最高
-
Group命名规范
- 使用有意义的名称:
{service_name}_consumer_group - 例如:
order_service_consumer_group
- 使用有意义的名称:
-
监控消费进度
- 监控每个Group的消费Lag
- 及时发现消费延迟
Q5: Push模式 vs Pull模式详解
Push模式(推送模式)
原理: Broker主动推送消息给Consumer
┌──────────┐ ┌──────────┐
│ Broker │ ──推送消息────▶ │ Consumer │
│ │ ◀──ACK确认──── │ │
└──────────┘ └──────────┘
优点:
- 实时性高,消息到达即推送
- 使用简单,无需轮询
- 自动消息分发
缺点:
- 可能压垮消费者(流量过大)
- 消费者无法控制消费速率
- 不易实现批量处理
适用场景:
- RabbitMQ默认模式
- 实时性要求高的场景
- 消息量可控的场景
代码示例(RabbitMQ):
java
@Component
public class OrderConsumer {
@RabbitListener(queues = "order.queue")
public void onMessage(Order order) {
// Broker主动推送,直接处理
processOrder(order);
}
}
Pull模式(拉取模式)
原理: Consumer主动从Broker拉取消息
┌──────────┐ ┌──────────┐
│ Consumer │ ──拉取请求────▶ │ Broker │
│ │ ◀──返回消息──── │ │
└──────────┘ └──────────┘
优点:
- 消费者控制消费速率,不会被压垮
- 可以批量拉取,提高效率
- 可以重复消费(回溯到指定offset)
- 消费者可以根据自身处理能力拉取
缺点:
- 可能产生空轮询(消息不存在时)
- 实时性取决于拉取频率
- 代码相对复杂
适用场景:
- Kafka、RocketMQ默认模式
- 大数据批量处理
- 需要控制消费速率的场景
代码示例(Kafka):
java
KafkaConsumer<String, String> consumer = new KafkaConsumer<>(props);
consumer.subscribe(Arrays.asList("order_topic"));
while (true) {
// 主动拉取消息,超时时间100ms
ConsumerRecords<String, String> records =
consumer.poll(Duration.ofMillis(100));
for (ConsumerRecord<String, String> record : records) {
processOrder(record.value());
}
// 手动提交offset
consumer.commitSync();
}
长轮询优化(RocketMQ):
java
// RocketMQ的Push实际是基于Pull的长轮询封装
// 如果没有消息,Broker会hold住请求,直到有消息或超时
@RocketMQMessageListener(topic = "order_topic")
public class OrderListener implements RocketMQListener<Order> {
@Override
public void onMessage(Order order) {
// 看起来像Push,实际是长轮询Pull
processOrder(order);
}
}
对比总结
| 维度 | Push模式 | Pull模式 |
|---|---|---|
| 实时性 | 高(立即推送) | 取决于拉取频率 |
| 消费速率 | Broker控制 | Consumer控制 |
| 系统压力 | 可能压垮Consumer | Consumer可控 |
| 批量处理 | 不易实现 | 容易实现 |
| 消息回溯 | 不支持 | 支持 |
| 空轮询 | 无 | 可能存在 |
| 实现复杂度 | 简单 | 相对复杂 |
| 代表产品 | RabbitMQ | Kafka, RocketMQ |
Q6: 集群模式 vs 广播模式
集群模式(Clustering / Shared Subscription)
定义: 同一Consumer Group内的消费者负载均衡消费消息
特点:
- 一条消息只被Group内的一个消费者消费
- 实现负载均衡
- 提高消费吞吐量
示例:
Topic: order_topic
Consumer Group: order_service
Message1 → Consumer1 ✓
Message2 → Consumer2 ✓
Message3 → Consumer3 ✓
Message4 → Consumer1 ✓
RocketMQ配置:
java
@RocketMQMessageListener(
topic = "order_topic",
consumerGroup = "order_service",
messageModel = MessageModel.CLUSTERING // 集群模式(默认)
)
public class OrderConsumer implements RocketMQListener<Order> {
@Override
public void onMessage(Order order) {
// 消息被Group内的某一个Consumer消费
processOrder(order);
}
}
适用场景:
- 业务消息处理
- 需要负载均衡
- 提高消费能力
广播模式(Broadcasting)
定义: 每个消费者实例都接收所有消息
特点:
- 每个Consumer实例都收到相同的消息
- 实现消息广播
- 用于全局通知
示例:
Topic: config_update
Consumer Instances: app1, app2, app3
ConfigUpdate Message → app1 ✓
→ app2 ✓
→ app3 ✓
RocketMQ配置:
java
@RocketMQMessageListener(
topic = "config_update",
consumerGroup = "app_instance_" + UUID.randomUUID(), // 每个实例唯一
messageModel = MessageModel.BROADCASTING // 广播模式
)
public class ConfigListener implements RocketMQListener<Config> {
@Override
public void onMessage(Config config) {
// 每个实例都会收到消息
refreshLocalConfig(config);
}
}
Kafka实现广播:
java
// Kafka通过让每个消费者使用不同的Group ID实现广播
String uniqueGroupId = "config_consumer_" + UUID.randomUUID();
props.put("group.id", uniqueGroupId);
KafkaConsumer<String, Config> consumer = new KafkaConsumer<>(props);
consumer.subscribe(Collections.singletonList("config_topic"));
适用场景:
- 配置更新通知
- 缓存刷新
- 系统广播通知
- 服务器状态同步
对比总结
| 维度 | 集群模式 | 广播模式 |
|---|---|---|
| 消息消费 | 一个Group内只有一个Consumer消费 | 每个Consumer都消费 |
| 负载均衡 | 是 | 否 |
| 消费重复 | 否 | 是(每个实例都消费) |
| 吞吐量 | 高(多Consumer分担) | 低(每个都要处理) |
| 适用场景 | 业务处理 | 全局通知 |
| Offset管理 | Broker统一管理 | 各Consumer独立管理 |
Q7: 什么是Exchange?(RabbitMQ特有概念)
Exchange定义
Exchange(交换机)是RabbitMQ特有的概念,接收生产者的消息,根据路由规则分发到队列。
四种Exchange类型
1. Direct Exchange(直连交换机)
特点: 根据routing key精确匹配
Producer → Exchange(direct)
├─ routing_key="order.create" → Queue1
├─ routing_key="order.pay" → Queue2
└─ routing_key="order.cancel" → Queue3
代码示例:
java
@Configuration
public class DirectExchangeConfig {
@Bean
public DirectExchange orderExchange() {
return new DirectExchange("order.direct.exchange");
}
@Bean
public Queue createQueue() {
return new Queue("order.create.queue");
}
@Bean
public Binding createBinding() {
return BindingBuilder
.bind(createQueue())
.to(orderExchange())
.with("order.create"); // routing key精确匹配
}
}
// 发送
rabbitTemplate.convertAndSend("order.direct.exchange", "order.create", order);
适用场景: 需要精确路由的场景
2. Fanout Exchange(扇出交换机)
特点: 广播给所有绑定的队列,忽略routing key
Producer → Exchange(fanout) → Queue1
→ Queue2
→ Queue3
代码示例:
java
@Configuration
public class FanoutExchangeConfig {
@Bean
public FanoutExchange notifyExchange() {
return new FanoutExchange("notify.fanout.exchange");
}
@Bean
public Binding emailBinding() {
return BindingBuilder.bind(emailQueue()).to(notifyExchange());
}
@Bean
public Binding smsBinding() {
return BindingBuilder.bind(smsQueue()).to(notifyExchange());
}
}
// 发送(routing key无效)
rabbitTemplate.convertAndSend("notify.fanout.exchange", "", notification);
适用场景: 消息广播、群发通知
3. Topic Exchange(主题交换机)
特点: 根据routing key模式匹配
-
*匹配一个单词 -
#匹配0个或多个单词Producer → Exchange(topic)
├─ "order.create.success" → Queue1 (order..success)
├─ "order.pay.failed" → Queue2 (order.#)
└─ "user.register.success" → Queue3 (.*.success)
代码示例:
java
@Configuration
public class TopicExchangeConfig {
@Bean
public TopicExchange logExchange() {
return new TopicExchange("log.topic.exchange");
}
@Bean
public Binding errorLogBinding() {
return BindingBuilder
.bind(errorQueue())
.to(logExchange())
.with("log.error.*"); // 匹配 log.error.xxx
}
@Bean
public Binding allLogBinding() {
return BindingBuilder
.bind(allQueue())
.to(logExchange())
.with("log.#"); // 匹配 log.xxx.xxx.xxx
}
}
// 发送
rabbitTemplate.convertAndSend("log.topic.exchange", "log.error.database", logMsg);
适用场景: 灵活的消息路由、日志分级处理
4. Headers Exchange(头交换机)
特点: 根据消息头属性匹配,不常用
java
@Bean
public Binding headersBinding() {
Map<String, Object> headers = new HashMap<>();
headers.put("format", "pdf");
headers.put("type", "report");
return BindingBuilder
.bind(reportQueue())
.to(headersExchange())
.whereAll(headers).match(); // 所有header都匹配
}
Exchange对比
| 类型 | 路由方式 | 性能 | 适用场景 |
|---|---|---|---|
| Direct | routing key精确匹配 | 高 | 精确路由 |
| Fanout | 忽略routing key,广播 | 最高 | 消息广播 |
| Topic | routing key模式匹配 | 中 | 灵活路由 |
| Headers | 消息头匹配 | 低 | 复杂条件 |
Q8-Q15: 更多基础概念
Q8: 什么是消息的持久化?
三层持久化(RabbitMQ)
1. Exchange持久化
java
DirectExchange exchange = new DirectExchange("order.exchange", true, false);
// 第二个参数 true 表示持久化
2. Queue持久化
java
Queue queue = new Queue("order.queue", true);
// 第二个参数 true 表示持久化
3. Message持久化
java
MessageProperties props = MessageProperties.PERSISTENT_TEXT_PLAIN;
Message message = new Message(body, props);
重要: 三层都持久化才能保证消息不丢失
RocketMQ/Kafka持久化
- 默认持久化到磁盘
- 通过刷盘策略控制:同步刷盘 vs 异步刷盘
Q9: 什么是消息的优先级?
RabbitMQ实现
队列设置最大优先级:
java
Map<String, Object> args = new HashMap<>();
args.put("x-max-priority", 10); // 支持0-10优先级
channel.queueDeclare("priority.queue", true, false, false, args);
消息设置优先级:
java
AMQP.BasicProperties props = new AMQP.BasicProperties.Builder()
.priority(5) // 设置优先级5
.build();
channel.basicPublish("", "priority.queue", props, message.getBytes());
限制
- 队列为空时优先级无效
- 影响性能
- RocketMQ/Kafka不原生支持
替代方案
使用多个Topic/Queue模拟不同优先级:
high_priority_topicnormal_priority_topiclow_priority_topic
Q10: 什么是消息的过期时间(TTL)?
两种设置方式
1. 队列级别TTL(所有消息统一过期时间)
java
@Bean
public Queue orderQueue() {
return QueueBuilder.durable("order.queue")
.ttl(60000) // 60秒
.build();
}
2. 消息级别TTL(单条消息独立过期时间)
java
MessageProperties props = new MessageProperties();
props.setExpiration("30000"); // 30秒
rabbitTemplate.convertAndSend("order.exchange", "order.key", order, message -> {
message.getMessageProperties().setExpiration("30000");
return message;
});
过期后处理
RabbitMQ:
- 进入死信队列(如果配置了DLX)
- 或直接删除
RocketMQ:
- 不会立即删除
- 消费时判断是否过期
Q11: 什么是消息确认机制(ACK)?
自动ACK
java
@RabbitListener(queues = "order.queue", ackMode = "AUTO")
public void onMessage(Order order) {
// 消息发送给消费者就自动确认
// 风险:处理失败消息丢失
}
手动ACK(推荐)
java
@RabbitListener(queues = "order.queue", ackMode = "MANUAL")
public void onMessage(Order order, Channel channel,
@Header(AmqpHeaders.DELIVERY_TAG) long tag) throws IOException {
try {
processOrder(order);
channel.basicAck(tag, false); // 手动确认
} catch (Exception e) {
channel.basicNack(tag, false, true); // 拒绝并重新入队
}
}
NACK/Reject
java
// basicNack(可批量)
channel.basicNack(tag, false, true); // requeue=true重新入队
// basicReject(单条)
channel.basicReject(tag, false); // requeue=false不重新入队,进死信
Q12: 什么是预取(Prefetch)?
定义
Consumer一次从Broker预取多少条消息
设置方法
java
// RabbitMQ
channel.basicQos(100); // 预取100条
// Kafka
props.put("max.poll.records", 500); // 一次拉取500条
设置建议
| 场景 | 建议值 | 原因 |
|---|---|---|
| 处理快 | 100+ | 提高吞吐量 |
| 处理慢 | 1-10 | 避免消息积压在Consumer |
| 顺序消费 | 1 | 保证顺序 |
Q13: 什么是消息路由?
RabbitMQ路由
通过 Exchange + Routing Key 实现
java
// 发送时指定routing key
rabbitTemplate.convertAndSend("order.exchange", "order.create", order);
// 绑定时指定routing key
BindingBuilder.bind(queue).to(exchange).with("order.create");
Kafka路由
通过 Partition Key 实现
java
ProducerRecord<String, Order> record = new ProducerRecord<>(
"order_topic",
order.getUserId().toString(), // Partition Key
order
);
// 相同Key的消息进入同一分区
producer.send(record);
RocketMQ路由
通过 MessageQueueSelector 选择Queue
java
producer.send(message, new MessageQueueSelector() {
@Override
public MessageQueue select(List<MessageQueue> mqs,
Message msg, Object arg) {
Long userId = (Long) arg;
int index = (int) (userId % mqs.size());
return mqs.get(index);
}
}, order.getUserId());
Q14: 什么是NameServer?(RocketMQ特有)
作用
- 服务注册与发现:Broker注册到NameServer
- 路由信息管理:维护Topic与Broker的映射关系
- 轻量级设计:无状态,易于扩展
vs ZooKeeper
| 维度 | NameServer | ZooKeeper |
|---|---|---|
| 一致性 | 最终一致性 | 强一致性(CP) |
| 性能 | 高 | 相对低 |
| 复杂度 | 低 | 高 |
| 功能 | 专注于路由 | 分布式协调 |
架构
Producer/Consumer
↓ 查询路由
NameServer集群(任意一台)
↑ 注册
Broker集群
Q15: 消息队列的应用场景有哪些?
1. 异步处理
- 用户注册(发邮件、短信)
- 订单处理(通知物流、财务)
- 图片/视频处理(上传后异步转码)
2. 系统解耦
- 订单系统与库存、积分系统解耦
- 支付系统与订单系统解耦
- 新增下游系统无需修改上游
3. 流量削峰
- 秒杀活动
- 抢购场景
- 防止数据库被打垮
4. 日志收集
- ELK架构(Elasticsearch + Logstash + Kafka)
- 应用日志收集
- 系统监控指标收集
5. 数据同步
- 主从数据库同步
- 缓存与数据库同步
- 跨系统数据同步
6. 事件驱动
- 微服务事件总线
- 领域事件发布订阅
- CQRS架构
7. 任务调度
- 异步任务处理
- 定时任务触发
- 批量任务处理
8. 消息通知
- 邮件批量发送
- 短信批量发送
- APP推送通知