系统设计 Lab
通知系统是一条 fan-out 流水线,它最难的活儿是把快速的 producer 和慢且不可靠的 provider 解耦开。
调节请求速率、每条通知的 fan-out、每条通知的 channel 数、用户规模、provider latency、retry 次数和每用户发送上限。设计会从直接的同步发送演进到带 channel worker 的持久化 queue、多 channel 路由、dedup 加 rate limit 加偏好过滤,最后是带 dead-letter queue 和投递分析的大流量 retry。
分步讲解
一步一步把它想清楚
要点
常规演进场景
从左到右点击,就是预设的演示路径。每张卡片都会改变 workload 输入。
推荐形态
当前架构路径
Producer
Producer 请求发出一条通知的各种服务和触发器
Ingest API
Ingest API 接收请求、校验它们,并快速返回,不会卡在某次发送上
Preference store 保存每用户的 channel 选择、退订和免打扰时间窗
Queue + worker
Message queue 持久化缓冲,让 producer 永远不会卡在慢 provider 上
Channel worker 拉取消息,做 dedup 和 rate limit,再按 channel 分发
Provider adapter
Push adapter 通过 Apple 和 Google 的网关发送移动 push
Email adapter 通过一个事务型 email provider 发送邮件
SMS adapter 通过一个电信 provider 发送 SMS
Retry + 分析
Retry + DLQ 用 backoff 重试失败的发送,并把发不出去的停进一个 dead-letter queue
投递分析 在热路径之外收集投递、退信和失败事件供看板使用
瓶颈
Queue 积压压力
Provider 受限的 throughput
Fan-out 放大
Retry / DLQ 压力
Dedup / rate limit 负载
为什么会变
决策权衡
有出处支撑的规则
这些是模型背后那些经得起时间考验的 system design 论断。而 slider 的具体阈值,则被刻意标注为教学用的假设。
queue 把快速的 producer 和慢速的 consumer 解耦开
在 producer 和调用慢 provider 的发送者之间放一个持久化 queue,能抹平突发流量,并阻止某个 provider 卡顿堵住请求路径。
AWS — Queue-Based Load Leveling失败的投递在 retry 之后该进 dead-letter queue
在有限次处理失败后,消息会被挪到 dead-letter queue,这样 poison message 就堵不住主 queue,之后还能拿来排查。
AWS SQS Dead-Letter Queues带 jitter 的指数 backoff 能避免 retry 风暴
用指数增长、带抖动的延迟去重试失败的 provider 调用,可以防止同步的 retry 把一个本就吃力的 provider 彻底压垮。
AWS Architecture BlogAPNs 和 FCM 是移动 push 的网关
移动 push 是经 provider 网关投递的(Apple 用 APNs,Android 用 FCM),而非直接发到设备,所以 push adapter 必须会说每种网关协议。
Apple APNs Documentation教学用假设
- 每次 channel 发送都建模成一次外部 provider 调用;总发送数 = 请求数 × fan-out × channel 数。
- provider 并发是有上限的,所以可达到的发送速率按「worker 并发 / provider latency」缩放;约束在 latency,不在 CPU。
- dedup 和 rate limit 近似为对每个候选发送做一次 per-user 计数器查询;偏好过滤则是分发前的一次 per-user 读取。