同步 vs 异步:没有 MQ vs 有 MQ
用户下单
扣减库存
发送通知
更新统计
总耗时 = A + B + C + D | 任何服务挂掉,整条链路失败
用户下单
Message Queue
总耗时 ≈ A + 写入MQ | 服务独立运行,互不影响
没有 MQ = 你亲自跑银行排队。你要转账、理财、办卡,每个业务都要排队等前一个办完才能办下一个。你被「阻塞」了,一步卡住后面全卡住。
有 MQ = 用快递。你把包裹丢到快递柜(队列),就可以去做别的事了。快递员(消费者)按自己的节奏取件、配送。你不需要等快递员,快递员也不需要等你。双方完全解耦。
三大核心价值
新增服务只需订阅队列,
无需修改上游代码
用户等待: 355ms → 5ms
响应速度提升 70 倍
高峰请求全部接住,
下游按能力平稳消费
生产场景
场景:双十一秒杀,订单系统瞬间涌入 5000 订单/秒,但数据库写入能力只有 500/秒。
不使用 MQ:数据库连接池耗尽 → 请求超时 → 雪崩 → 整个系统崩溃。
使用 MQ:订单先写入消息队列(内存级速度),消费者以数据库能承受的速度匀速消费。高峰期积压的消息在平峰期慢慢消化完。
// 伪代码:削峰保护
app.post('/order', async (req, res) => {
// 秒级响应 — 只写MQ
await mq.send('order_queue', orderData);
res.json({ msg: '订单已接受,处理中' });
});
// 消费者匀速消费
mq.consume('order_queue', async (msg) => {
await db.insert('orders', msg); // 数据库不扛压
}, { prefetch: 50 }); // 限制消费速率
Point-to-Point (点对点)
生产者
消息 A | B | C | D
消息被分配给不同消费者,每条消息只被消费一次
Publish-Subscribe (发布订阅)
发布者
主题
每条消息被所有订阅者各消费一次
Topic vs Queue 对比
| 维度 | Queue (队列) | Topic (主题) |
|---|---|---|
| 消费模式 | 竞争消费,一条消息一个消费者 | 每条消息广播给所有订阅者 |
| 消费者关系 | 互斥,同组只有一个能收到 | 独立,每个订阅者各收到一份 |
| 典型场景 | 任务分发、工作队列 | 事件通知、日志分发 |
| 代表产品 | RabbitMQ Queue | Kafka Topic |
Consumer Groups (消费者组)
同组内:竞争消费,一条消息只被组内一个消费者处理(负载均衡)。
不同组间:每条消息被每个组各消费一次(发布订阅)。
这是 Kafka 的核心模型。比如 "订单创建" 消息 → 库存组 减库存,通知组 发短信,统计组 更新报表。各组独立消费、互不影响。
架构总览
生产者
交换机 (路由)
Exchange 负责路由规则,消息不会直接发到 Queue,而是先经过 Exchange
Exchange 类型 — 四种路由策略
Direct Exchange (直连)
精确匹配 routing key,一对一路由
Fanout Exchange (广播)
忽略 routing key,广播到所有绑定队列
Topic Exchange (主题)
支持通配符:* 一个词,# 零或多个词
Headers Exchange (头部)
根据消息 headers 属性匹配,忽略 routing key
消息确认 (ACK / NACK)
ACK (确认):消费者处理完消息后发送 ack,RabbitMQ 才将消息从队列删除。
NACK (拒绝):消费者处理失败发送 nack,可指定是否重新入队 requeue=true。
关键配置:开启手动确认 { noAck: false },否则消息发出后立刻删除(可能丢失)。
// 手动 ACK 模式
channel.consume('orders', (msg) => {
try {
processOrder(msg);
channel.ack(msg); // 处理成功 → 确认
} catch (err) {
channel.nack(msg, false, true); // 失败 → 重新入队
}
}, { noAck: false });
死信队列 (Dead Letter Queue)
TTL=30min, max-length=1000
且不重新入队
过期
最大长度
DLX (Dead Letter Exchange)
人工处理 / 告警 / 重试
生产案例:订单支付超时自动取消
需求:用户下单后 30 分钟未支付,自动取消订单。
实现方案:
- 创建普通队列
order_ttl_queue,设置 TTL = 30 分钟,绑定死信交换机 - 创建死信队列
order_cancel_queue,消费者监听此队列 - 用户下单 → 消息发到
order_ttl_queue(没有消费者) - 30 分钟后消息过期 → 自动进入死信队列 → 消费者处理取消逻辑
// Java (Spring AMQP) 实现:订单超时自动取消
@Configuration
public class RabbitMQConfig {
// 死信交换机
@Bean
public DirectExchange dlxExchange() {
return new DirectExchange("dlx_exchange");
}
// 死信队列 — 消费者监听此队列
@Bean
public Queue orderCancelQueue() {
return QueueBuilder.durable("order_cancel_queue")
.withArgument("x-dead-letter-exchange", "")
.build();
}
@Bean
public Binding dlxBinding() {
return BindingBuilder.bind(orderCancelQueue())
.to(dlxExchange()).with("order_cancel");
}
// TTL 队列 — 消息30分钟后过期,转入死信
@Bean
public Queue orderTtlQueue() {
return QueueBuilder.durable("order_ttl_queue")
.withArgument("x-message-ttl", 1800000) // 30分钟
.withArgument("x-dead-letter-exchange", "dlx_exchange")
.withArgument("x-dead-letter-routing-key", "order_cancel")
.build();
}
}
// 生产者:下单时发送消息到TTL队列
@Service
public class OrderService {
@Autowired
private RabbitTemplate rabbitTemplate;
public void placeOrder(String orderId) {
rabbitTemplate.convertAndSend("",
"order_ttl_queue",
new OrderMessage(orderId, System.currentTimeMillis()));
// 消息进入TTL队列 → 30分钟后过期 → 自动转入死信队列
}
}
// 消费者:监听死信队列,执行取消
@Component
public class OrderCancelConsumer {
@RabbitListener(queues = "order_cancel_queue")
public void handleCancel(OrderMessage msg, Channel channel,
Message message) throws IOException {
try {
cancelOrder(msg.getOrderId()); // 取消订单
channel.basicAck(message.getMessageProperties()
.getDeliveryTag(), false); // 手动ACK
} catch (Exception e) {
channel.basicNack(message.getMessageProperties()
.getDeliveryTag(), false, true); // 重新入队
}
}
}
完整 Java (Spring AMQP) 示例
// application.yml 配置
spring:
rabbitmq:
host: localhost
port: 5672
publisher-confirm-type: correlated # 发布确认
publisher-returns: true
// Producer — 发送消息
@Service
public class TaskProducer {
@Autowired
private RabbitTemplate rabbitTemplate;
public void sendTask(String task) {
// 持久化消息:MessageDeliveryMode.PERSISTENT
rabbitTemplate.convertAndSend("task_exchange",
"task.routing.key", task, message -> {
message.getMessageProperties()
.setDeliveryMode(MessageDeliveryMode.PERSISTENT);
return message;
});
System.out.println(" [x] Sent: " + task);
}
}
// Consumer — 消费消息
@Component
public class TaskConsumer {
// prefetch=1:公平分发,同时只处理1条
@RabbitListener(
queues = "task_queue",
concurrency = "1", // 单消费者
ackMode = "MANUAL" // 手动ACK
)
public void handleTask(String task, Channel channel,
Message message) throws IOException {
long tag = message.getMessageProperties()
.getDeliveryTag();
System.out.println(" [x] Received: " + task);
try {
// 模拟处理耗时
Thread.sleep(1000);
System.out.println(" [x] Done");
channel.basicAck(tag, false); // 确认
} catch (Exception e) {
// 处理失败 → 拒绝 → 重新入队
channel.basicNack(tag, false, true);
}
}
}
架构总览
P0
P1
P2
每个 Partition 只能被组内一个 Consumer 消费 → 负载均衡 + 有序性保证
Partition & Offset
Partition(分区):一个 Topic 被分成多个 Partition,分布在不同的 Broker 上。分区是并行度的基本单位。
Offset(偏移量):每条消息在 Partition 内的唯一递增编号。Consumer 通过 offset 记录消费进度。
有序性保证:同一 Partition 内消息严格有序;不同 Partition 之间不保证顺序。
为什么 Kafka 这么快?
传统方式:磁盘 → 内核缓冲区 → 用户缓冲区 → Socket 缓冲区 → 网卡 (4 次拷贝)
零拷贝 (sendfile):磁盘 → 内核缓冲区 → 网卡 (2 次拷贝,CPU 不参与数据搬运)
Kafka 使用 Linux 的 sendfile() 系统调用,直接在内核空间完成数据传输。
Kafka vs RabbitMQ
| 维度 | RabbitMQ | Kafka |
|---|---|---|
| 定位 | 消息代理 (Broker) | 分布式流平台 |
| 语言实现 | Erlang | Scala / Java |
| 吞吐量 | 万级/秒 | 百万级/秒 |
| 延迟 | 微秒级 | 毫秒级 |
| 消息模型 | Exchange → Queue | Topic → Partition |
| 消息顺序 | 单队列有序 | 单分区有序 |
| 路由能力 | 非常灵活 (4 种 Exchange) | 基于 Topic 分区 |
| 消息持久化 | 可选 (内存/磁盘) | 默认持久化 (日志文件) |
| 回溯消费 | 不支持 (消费即删除) | 支持 (通过 offset 重置) |
| 适用场景 | 任务队列、复杂路由 | 日志收集、流处理 |
生产案例:日志收集系统
架构:
Filebeat
10+ 亿条/天
实时计算
日志检索
归档存储
为什么选 Kafka:每天 10 亿条 ≈ 12,000 条/秒(平均),高峰可达 50,000+ 条/秒。需要高吞吐 + 持久化 + 多消费者组。RabbitMQ 在这个量级下扛不住。
什么时候用什么?
- 复杂路由需求(多种 Exchange)
- 任务队列 / 工作队列
- 需要消息确认、优先级队列
- 中小规模 (万级 TPS 足够)
- 延迟敏感(微秒级)
- 需要 TTL、死信队列
- 高吞吐量需求 (百万级 TPS)
- 日志收集、事件流处理
- 需要消息回溯 (重新消费)
- 大数据生态 (Flink, Spark)
- 事件溯源 / CQRS
- 消息持久化 + 长期存储
Redis 作为消息队列
Redis Streams 提供了类似 Kafka 的消费者组模型,适合轻量级消息队列场景。
# 添加消息 (相当于 Kafka Producer)
XADD orders * orderId ORD-001 userId U-1001 amount 99.9
# 返回: "1704067200000-0" (自动生成的时间戳 ID)
# 创建消费者组
XGROUP CREATE orders order-group $ MKSTREAM
# 消费者读取消息
XREADGROUP GROUP order-group consumer-1 COUNT 1 BLOCK 5000 STREAMS orders >
# > 表示只读取未消费的新消息
# 确认消息 (相当于 ACK)
XACK orders order-group 1704067200000-0
# 查看待处理消息 (查看积压)
XPENDING orders order-group
Redis Streams 的局限
| 维度 | Redis Streams | RabbitMQ / Kafka |
|---|---|---|
| 吞吐量上限 | 受 Redis 单线程限制 | 分布式水平扩展 |
| 持久化 | 内存为主,RDB/AOF 可能丢数据 | 磁盘持久化,数据安全 |
| 消息堆积 | 受内存限制 | 磁盘存储,可堆积 TB 级 |
| 路由能力 | 无 (只有 Stream 名) | RabbitMQ: 丰富 / Kafka: Topic |
| 监控运维 | 简单 | 成熟的监控生态 |
什么时候够用?什么时候升级?
- QPS < 5000,消息量级小
- 团队已经在用 Redis,不想加中间件
- 不需要复杂路由
- 消息丢失可容忍或业务可补偿
- 短期项目 / MVP
- QPS > 10000 或快速增长中
- 消息不能丢 (金融、订单)
- 需要消息堆积能力
- 需要复杂路由规则
- 需要多团队共享消息集群
消息幂等性
网络抖动导致 ack 丢失 → MQ 重发消息 → 同一笔订单扣了两次库存。
不保证幂等 = 重复消费 = 数据错误 = 生产事故
// 幂等消费伪代码
async function consumeMessage(msg) {
const msgId = msg.headers['message-id'];
// 去重检查 (Redis 或数据库)
const exists = await redis.set(`dedup:${msgId}`, 1, 'NX', 'EX', 86400);
if (!exists) {
console.log('重复消息,跳过', msgId);
return ack(msg);
}
// 执行业务逻辑
await processOrder(msg.body);
return ack(msg);
}
消息顺序性
分区有序:同一个 Partition 内消息严格按写入顺序消费。这是 Kafka 默认保证的。
全局有序:整个 Topic 所有消息有序。代价极大 — 只能有一个 Partition,完全丧失并行性。
最佳实践:将需要有序的消息发到同一个 Partition(通过 key hash 路由)。
消息可靠性
// RabbitMQ 生产者确认
ch.confirmSelect(); // 开启 confirm 模式
ch.waitForConfirms(); // 等待 Broker 确认
// 队列持久化
ch.assertQueue('orders', { durable: true });
// 消息持久化
ch.sendToQueue('orders', Buffer.from(msg), { persistent: true });
延迟消息
| 方案 | 原理 | 适用 |
|---|---|---|
| RabbitMQ TTL + DLX | 消息过期后进入死信队列 | 精确延迟,量级中等 |
| Kafka 时间轮 | 内部时间轮定时器 | 高吞吐延迟 |
| Redis ZSET | score 为执行时间,定时轮询 | 轻量级,简单场景 |
| 数据库轮询 | 定时扫表找过期记录 | 最简单,性能差 |
| RocketMQ 延迟级别 | 内置 18 个延迟级别 | Java 生态首选 |
消息积压
凌晨发现队列积压了 500 万条消息,消费者处理速度远远跟不上。
原因:消费者 Bug / 流量突增
临时加 10 倍 Consumer
写程序快速转发到新队列
保核心业务,丢日志类
增加 Partition / 优化消费逻辑 / 预警监控
- 监控告警:队列深度 > 阈值时立即告警 (如积压 > 10000 条)
- 合理的 prefetch:RabbitMQ 设置
prefetch,防止消费者被压垮 - 批量消费:消费者一次拉取多条,减少网络往返
- 容量规划:提前评估高峰流量,预留消费能力
- 降级方案:积压严重时丢弃非核心消息,保证核心业务
三方对比总表
| 维度 | RabbitMQ | Kafka | Redis Streams |
|---|---|---|---|
| 定位 | 消息代理 | 分布式流平台 | 轻量消息队列 |
| 语言 | Erlang | Scala/Java | C |
| 吞吐量 | ~万级 TPS | ~百万级 TPS | ~万级 TPS |
| 延迟 | 微秒级 | 毫秒级 | 微秒级 |
| 持久化 | 可选 | 默认磁盘 | 内存为主 |
| 路由能力 | ★★★★★ | ★★☆☆☆ | ★☆☆☆☆ |
| 消息回溯 | 不支持 | 支持 | 有限支持 |
| 运维复杂度 | 中等 | 较高 | 低 |
| 客户端生态 | 丰富 | 丰富 | Redis 生态 |
| 适用场景 | 任务队列/路由 | 日志/流处理 | 轻量队列 |
三大核心价值:
- 解耦:生产者和消费者通过队列解耦,新增消费者无需修改生产者代码。
- 异步:耗时操作异步处理,用户请求无需等待全部完成,响应更快。
- 削峰:高峰期请求先入队列缓冲,下游按自身能力匀速消费,防止系统崩溃。
类比:没有 MQ 像亲自跑银行排队(同步阻塞),有 MQ 像用快递(异步解耦)。
RabbitMQ:消息代理,支持复杂路由(4 种 Exchange),延迟低(微秒级),适合任务队列、复杂路由场景。吞吐量万级。
Kafka:分布式流平台,高吞吐(百万级 TPS),默认持久化,支持消息回溯。适合日志收集、流处理、大数据场景。
选型原则:需要复杂路由 → RabbitMQ;需要高吞吐 → Kafka;轻量级 → Redis Streams。
定义:同一条消息被消费多次,结果和消费一次一样。
为什么需要:网络抖动导致 ACK 丢失,MQ 会重发消息。不保证幂等 → 重复扣款、重复发货。
实现方案:
- 唯一 ID + 去重表:每条消息带唯一 ID,消费前查去重表,已存在则跳过。
- 乐观锁/版本号:更新时带版本号条件
UPDATE ... WHERE version=v。 - Redis SET NX:
SET dedup:msgId 1 NX EX 86400,设置成功才执行。
Kafka:同一 Partition 内消息严格有序。将需要有序的消息用相同的 key 发送(路由到同一 Partition)。全局有序只需一个 Partition(牺牲并行性)。
RabbitMQ:单队列有序。但多消费者竞争消费会破坏顺序,需确保顺序消息只由一个消费者处理。
最佳实践:分区有序(按 key 路由),而非全局有序。
三个环节都要保障:
- 生产者:使用 confirm 模式(RabbitMQ)或 acks=all(Kafka),确保消息成功写入 Broker。
- Broker:队列持久化 + 消息持久化。Kafka 默认持久化到磁盘。
- 消费者:手动 ACK,处理成功后才确认。失败则 NACK 重新入队。
定义:无法被正常消费的消息会被转发到死信队列(Dead Letter Queue),用于统一处理异常消息。
消息变成死信的条件:
- 消费者 NACK 且不重新入队
- 消息 TTL 过期
- 队列超过最大长度
典型场景:订单支付超时自动取消(TTL 30 分钟 + 死信队列)、异常消息人工处理、告警通知。
应急方案:
- 紧急扩容:临时增加消费者实例(10 倍),快速消化积压。
- 转发分流:写程序将积压消息快速转发到多个新队列,多队列并行消费。
- 丢弃低优先级:非核心消息(如日志)直接丢弃,保核心业务。
长期预防:监控告警 + 合理 prefetch + 批量消费 + 容量规划。
- Direct:精确匹配 routing key,点对点路由。
- Fanout:广播到所有绑定队列,忽略 routing key。
- Topic:支持通配符匹配(* 匹配一个词,# 匹配零或多个词)。
- Headers:根据消息 headers 属性匹配,不常用。
- 顺序写磁盘:追加写入日志文件,速度可达 600MB/s(随机写只有 100KB/s)。
- 零拷贝 (sendfile):数据从磁盘直接到网卡,跳过用户态拷贝(4 次 → 2 次)。
- 批量发送 + 压缩:Producer 批量聚合消息,减少网络 I/O。
- PageCache:利用操作系统页缓存,热点数据直接从内存读取。
- 分区并行:Topic 分成多个 Partition,分布在不同 Broker 上并行读写。
Consumer Group 内:一个 Partition 只能被组内一个 Consumer 消费(负载均衡)。
Consumer Group 间:每个组独立消费同一 Topic 的全部消息(发布订阅)。
关键限制:Consumer 数量 > Partition 数量时,多余的 Consumer 会空闲。扩容消费者前先扩容 Partition。
常见方案:
- RabbitMQ TTL + DLX:消息设 TTL,过期后进入死信队列,消费者监听死信队列。
- Redis ZSET:score 为执行时间戳,定时轮询 score <= now 的消息。
- Kafka 时间轮:内部 DelayQueue + TimingWheel 实现。
- 数据库轮询:最简单但性能差,适合低频场景。
- RocketMQ:内置延迟级别(18 级),开箱即用。
定义:当消费者处理速度跟不上生产速度时,通过反馈机制告知生产者降低发送速率,防止系统过载。
RabbitMQ:通过 prefetch 限制未确认消息数量。达到上限后不再推送,天然背压。
Kafka:消费者自己控制拉取速率(pull 模式),可以暂停消费。但没有自动背压机制,需在应用层实现。
三个条件缺一不可:
- Exchange 持久化:
assertExchange(name, type, { durable: true }) - Queue 持久化:
assertQueue(name, { durable: true }) - Message 持久化:
sendToQueue(name, msg, { persistent: true })
注意:持久化不保证 100% 不丢。消息先到 PageCache,还没刷盘就宕机会丢。需要更高可靠性用 publisher confirms。
Offset:消息在 Partition 内的唯一递增编号,标识消费进度。
管理方式:
- 自动提交:
enable.auto.commit=true,定期提交,可能丢消息或重复消费。 - 手动提交:
commitSync()或commitAsync(),处理完再提交,更精确。 - 指定 offset:
seek(partition, offset),可以回溯重新消费。
ACK (ack):告诉 Broker 消息处理成功,可以删除。消息的生命周期结束。
NACK (nack):告诉 Broker 消息处理失败。
requeue=true:消息重新入队,重新投递给消费者。requeue=false:消息变成死信(如果配置了 DLX)或被丢弃。
注意:NACK + requeue=true 可能导致消息无限循环重试。建议设置最大重试次数。
- 消息丢失:ACK 机制 + 持久化 + 生产者确认
- 消息重复:幂等消费(唯一 ID + 去重表)
- 消息积压:扩容消费者 + 监控告警
- 消息乱序:相同 Key 路由到同一 Partition
- 系统复杂度:引入 MQ 增加了运维成本,需要监控、 HA、故障恢复
点对点 (PTP):一条消息只被一个消费者消费。多个消费者竞争消费,消息在消费者间分配。适合任务分发。
发布订阅 (Pub/Sub):一条消息被所有订阅者各消费一次。每个订阅者独立接收完整消息。适合事件广播。
Kafka 的 Consumer Group:结合两者优势 — 组内竞争消费(PTP),组间广播(Pub/Sub)。
- 副本机制 (Replica):每个 Partition 有多个副本,分布在不同 Broker 上。
- Leader/Follower:读写都走 Leader,Follower 同步数据。Leader 宕机时 Follower 自动晋升。
- ISR (In-Sync Replicas):与 Leader 保持同步的副本集合。只有 ISR 中的副本才能成为新 Leader。
- Controller:Kafka 集群中有一个 Broker 担任 Controller,负责分区 Leader 选举、Broker 故障检测。
小规模场景可以:QPS < 5000、消息量小、已在用 Redis、不需要复杂路由时,Redis Streams 是轻量好选择。
不能替代的场景:
- 需要复杂路由(多种 Exchange)
- 消息不能丢失(Redis 内存为主,RDB/AOF 可能丢数据)
- 需要大量消息堆积(Redis 受内存限制)
- 需要成熟的运维监控生态
VHost (Virtual Host):RabbitMQ 中的逻辑隔离单位,类似于数据库中的"数据库"概念。
- 每个 VHost 有独立的 Exchange、Queue、Binding、权限
- 不同项目/环境可以用不同的 VHost 隔离
- 默认 VHost 是
/ - 用户可以被授权访问特定的 VHost
RabbitMQ:声明队列时设置 x-max-priority(如 10),发送消息时设置 priority 字段。高优先级消息优先被消费。
Kafka:没有原生优先级支持。可以通过不同 Topic 模拟(高优先级 Topic 优先消费),或在应用层实现。
// RabbitMQ 优先级队列
ch.assertQueue('tasks', {
arguments: { 'x-max-priority': 10 }
});
ch.sendToQueue('tasks', msg, { priority: 5 });
根本原因:消费者处理成功,但 ACK 未到达 Broker(网络超时、消费者宕机、Broker 重启)。Broker 认为消息未消费,重新投递。
常见场景:
- 消费者处理完消息后,发送 ACK 前宕机
- 网络抖动导致 ACK 丢失
- 消费者处理超时,Broker 认为消费失败重新投递
结论:任何消息队列都可能重复投递,消费者必须保证幂等。
Rebalance:Consumer Group 中有消费者加入或退出时,Partition 需要重新分配给剩余消费者。
问题:
- Rebalance 期间所有消费者暂停消费(Stop The World)
- 频繁 Rebalance 导致消费延迟
- 可能触发连锁 Rebalance(消费者超时 → Rebalance → 更多超时)
优化:
- 调大
session.timeout.ms和heartbeat.interval.ms - 使用
StickyAssignor减少分区迁移 - 确保消费者处理逻辑高效
- 运维成本:MQ 本身是分布式系统,需要 HA、监控、升级、故障恢复。
- 一致性问题:分布式事务难处理。生产者写库成功但 MQ 发送失败怎么办?需要补偿机制。
- 调试困难:异步链路比同步链路更难追踪。需要分布式追踪(如 Jaeger)。
- 消息堆积风险:消费者故障时消息堆积,可能导致内存溢出或磁盘满。
- 系统可用性:MQ 挂了 = 系统不可用。MQ 成了新的单点。
核心问题清单:
- 消息丢失:生产者确认 + Broker 持久化 + 消费者手动 ACK
- 消息重复:唯一 ID + 去重表 + 幂等消费
- 消息顺序:相同 Key → 相同 Partition,分区有序
- 消息延迟:TTL + 死信队列 / 时间轮 / Redis ZSET
- 消息积压:扩容消费者 + 监控告警 + 降级方案
- 消息回溯:Kafka offset 重置
- 高可用:RabbitMQ 镜像队列 / Kafka 副本机制
- 安全:认证、授权、VPC 隔离、消息加密
总结公式:消息不丢 + 不重 + 有序 + 可回溯 = 可靠消息系统。
★ RabbitMQ 集群高可用架构
三种集群模式对比
| 模式 | 原理 | 优缺点 |
|---|---|---|
| 普通集群 | 队列只存在一个节点,其他节点可转发请求 | 性能好但节点挂了队列不可用 ❌ |
| 镜像队列集群 (Mirrored Queue) |
队列在多个节点间同步复制 | 高可用,节点挂了自动切换 ✅ 但性能有损耗(同步开销) |
| 仲裁队列 (Quorum Queue, 3.8+) |
基于 Raft 协议的强一致队列 | 推荐新方案 ✅✅ 替代镜像队列,性能更好 |
面试回答技巧:先说"三种模式",再强调"3.8+ 推荐用 Quorum Queue 替代镜像队列",体现对新特性的关注。