日期:2026-09-27
标签:#面试 #场景设计 #消息队列 #Kafka
难度:简单
来源:牛面场景题
答案说明:独立整理(站内题目标记为 VIP,未读取会员答案)
一句话答案
一个业务事件要触发多个职责独立、演进节奏不同的下游处理时,适合通过发布订阅分发消息。
面试口语版(约 60 秒)
比如订单支付成功后,通知、积分、风控和数据分析都需要知道这件事。让订单服务逐个同步调用,链路会被最慢的下游拖住,新增下游也得改订单服务。这时可以发布一条带事件 ID、订单 ID、发生时间和版本的“支付成功”事件,让各下游按各自的消费者组订阅。Kafka 中不同消费者组可以各自消费同一主题;一个组内部则由成员分担分区,不是每个实例都收到一份。分发后要分别监控各组延迟、处理失败和重试,消费者对重复事件做幂等。若主流程必须等某项结果才能向用户承诺成功,那项操作仍应在主流程中完成或有明确的状态机。
原理与适用场景
flowchart LR
O[订单服务] -->|支付成功事件| K[Kafka 主题]
K --> N[通知消费者组]
K --> P[积分消费者组]
K --> R[风控消费者组]
P -->|分区 0| P1[组内实例 A]
P -->|分区 1| P2[组内实例 B]
图中以两个分区为例;组内实例分摊分区,不会各收到同一分区的完整副本。
- 跨组分发:每个消费者组维护自己的消费位置,可独立扩缩容、失败重试和回放。
- 组内分摊:同一分区在一个消费者组内同一时刻由一个成员处理;多个实例不是广播收件人。
- 事件契约:事件表述已经发生的事实,定义业务主键、事件 ID、版本与字段兼容规则。订阅者不要依赖生产者的内部表结构。
- 适合:同一事实供多个系统异步处理,且各结果允许在可约定的时间内达到一致。
具体案例与失败分支
积分服务故障时,通知和风控组仍可前进,积分组积压并在修复后按位点重放。重放可能重复执行发积分,因此积分流水以“支付事件 ID + 积分规则”设唯一约束。若通知需在请求返回前确认已发送,单纯分发无法满足这一同步承诺,应改为等待通知结果或把接口语义改成“已受理”。
取舍与易错点
- 分发减少生产者对下游数量的依赖,却增加事件契约、可观测性、追踪和最终一致性的成本。
- “同一主题有三个消费者实例”不等于“三份广播”;是否各得一份取决于消费者组。
- 先界定每类订阅方的失败策略。坏消息重试不能无限阻塞整个分区;隔离处理时仍要保留审计和补偿路径。
- 保持同一订单相关事件的键稳定,才有机会按分区维持顺序;Kafka 不承诺跨分区全局顺序。
面试官递进追问
- 一个主题与多个消费者组的关系是什么? 每组有独立位点,组内成员分摊分区。
- 新增订阅方需要改生产者吗? 契约兼容时通常不必;新组从哪里开始消费要按历史回放需求配置。
- 某个订阅方处理失败会怎样? 它的延迟与重试独立,但若共享关键下游或 Broker 资源仍可能互相影响,应做容量与隔离设计。
自测
- 用 60 秒说清“跨组分发”和“组内竞争”的区别。
- 画出订单支付事件分发给三个系统的图,并指出各系统重复消费的处理位置。
- 判断“支付成功但积分晚 2 分钟到账”是否可接受,写出需要与业务确认的承诺。
参考资料
核对日期:2026-09-27。