消息队列内部机制 可视化实验室
用交互式 SVG 呈现 Kafka 分区/副本、消费者组、RabbitMQ 交换器、消息确认等消息中间件核心机制。八个模块,纯原生实现。
Kafka 分区与副本
Topic 被切分为多个 Partition,每个 Partition 有一个 Leader 与若干 Follower 副本。ISR(In-Sync Replicas)是当前与 Leader 保持同步的副本集合。生产消息先写 Leader,再复制到 Followers。
分区与副本
点击“生产消息”观察消息写入 Leader 并复制到 ISR 副本;切换某副本失联可观察 ISR 收缩与 under-replicated 状态。acks=all 时需所有 ISR 副本确认才视为提交。
消费者组与 Offset
同一消费者组内每个 Partition 只被一个消费者消费,实现并行扩展。消费者维护 Offset 进度并提交。增减消费者触发 Rebalance,分区重新分配。点击观察消费推进与 Rebalance。
消费进度
每个分区当前消费 Offset 与已提交 Offset。Rebalance 后分区在消费者间重新分配,可能造成短暂重复消费(at-least-once)或需要 seek 恢复。auto.offset.commit=enable 时周期性提交。
RabbitMQ 交换器四种路由
Direct 按 routing key 精确匹配;Fanout 广播到所有绑定队列;Topic 按模式匹配(* 一词,# 零词或多词);Header 按消息头属性匹配。切换类型并发送消息观察路由差异。
Direct 交换器
routing key 与 binding key 完全相等才路由。输入 routing key 后发送,只有绑定了相同 key 的队列会收到消息。
消息确认 ACK / NACK 与幂等
消费者处理完成后 ACK 确认;失败则 NACK 并 requeue 重投,可能造成重复消费。通过去重表(幂等消费)实现 at-least-once 语义下业务 Exactly-Once。模拟消费者故障观察重投。
投递语义
auto-ack 关闭后,broker 在收到 ACK 前不会删除消息;消费者崩溃则消息 requeue 重投,导致 at-least-once。幂等消费用唯一 msg_id 在去重表标记,重复消息被跳过。
消息顺序性:分区有序 vs 全局有序
Kafka 仅保证单 Partition 内消息有序;多 Partition 并行消费会打破全局顺序。需要全局有序时只能用单 Partition(牺牲并行度)或顺序锁按 key 路由。切换模式对比。
顺序保证
分区有序:同一 key 路由到同一分区,分区内严格有序。全局有序:仅 1 个分区,所有消息串行,吞吐受限。顺序消费端用单线程 per-partition 消费保证处理顺序。
死信队列与重试退避
消费失败的消息经重试退避(指数退避)后仍失败,进入死信队列(DLQ)隔离处理。持续失败无法处理的消息称为毒丸(Poison Pill),需人工介入。模拟消费失败观察 DLQ 流转。
重试与死信
最大重试次数(如 3)内每次失败按指数退避(1s/2s/4s)延迟重投;超过阈值则转入 DLQ。毒丸消息永远失败,应设置最大重试避免无限重投阻塞队列。
消息积压与背压 Backpressure
生产速率大于消费速率时形成积压(Lag)。背压机制通过反馈调节生产速率或扩容消费者,防止系统过载。拖动滑块调节两侧速率观察积压与背压效果。
背压策略
背压实现:①拒绝/限流生产者(Reject / 429)②有界队列阻塞生产者线程③扩容消费者分区。当积压超过阈值(如 10000)触发背压,生产速率被压低至消费速率附近。
Exactly-Once 语义
Exactly-Once 通过事务消息(两阶段提交:Init→Begin→Produce→Commit)与幂等生产者(PID + 序列号去重)共同实现。对比 At-Least-Once 可能重复,Exactly-Once 保证消息不重不漏。
Exactly-Once 实现
幂等生产者:每个 producer 分配 PID,每条消息带递增 seq,broker 按 (PID, partition, seq) 去重,网络重试不会产生重复。事务:跨分区多消息原子写入,消费者 read_committed 只读已提交消息。