高并发通知系统设计笔记:如何安全发送百万级/亿级推送?

📅 2026/7/9 7:36:09
高并发通知系统设计笔记:如何安全发送百万级/亿级推送?
在大型促销如双11、Black Friday或突发事件发生时营销团队常常需要瞬间向百万甚至亿级用户推送通知。如果直接在请求处理程序中同步循环发送会导致请求线程阻塞、内存与 CPU 飙升最终压垮服务器。在 On-call 场景里也有类似需求比如 Status Page 服务发布故障信息就是一个巨大的邮件广播。比如很多人关注并订阅了 DeepSeek 的 Status Page如果 DeepSeek 故障Flashduty 就要给所有订阅用户发送邮件通知。高并发通知系统的核心设计原则是将“通知生成”与“通知分发”彻底解耦从追求“发送速度”转变为保障“系统安全”。核心架构设计1. 基于队列的异步处理系统引入消息队列如 Kafka、RabbitMQ作为缓冲区。流程API 接收到推送请求后仅负责将通知任务写入队列随后立即返回。优势队列能有效吸收瞬间的流量峰值。即使 100 万条通知在几秒内涌入后端 Worker 也可以按照自身能够承受的受控速率逐步消费并处理保护了应用层。2. 独立扩展的 Worker 集群将处理通知的 Worker Fleet工作节点集群与前端应用服务器分离实现独立弹性扩缩容。系统可根据队列堆积深度Queue Depth动态调整 Worker 数量。例如队列中有 1 万条消息时运行 10 个 Worker达到 100 万条时自动扩容至 1000 个 Worker而应用层始终不受影响。关键技术决策与陷阱应对陷阱一下游第三方服务限流Rate Limiting当我们拥有足够的 Worker 时瓶颈往往会转移到下游通知渠道如 Firebase、APNs、邮件服务商。如果盲目追求高并发会导致下游触发频率限制。决策在 Worker 层引入速率限制器Rate Limiter。即使队列中有海量数据Worker 也必须严格按照下游供应商允许的最大速率发送。此时队列积压是安全的比压垮依赖项要好得多。陷阱二分布式环境下的重复发送幂等性设计在分布式系统中“至少交付一次At-least-once”是常见网络策略。如果 Worker 成功发送了通知但在向队列提交 Confirm/Offset 之前崩溃队列会重新投递该消息导致用户收到重复通知。决策确保通知处理具备幂等性Idempotency。为每条通知生成全局唯一的notification_id。在调用下游发送前向数据库或 Redis尝试插入该 IDINSERTINTOprocessed_notifications(notification_id)VALUES(NOTIF-1001);利用数据库的唯一约束Unique Constraint若插入成功则执行发送若失败报主键冲突则说明已处理过直接跳过。陷阱三亿级100M超大流量过载当面对 1 亿以上用户时如果一次性把 1 亿条消息全部打入 Kafka会导致消息队列瞬间过载或 OOM。决策分阶段逐步生成。将“活动定义”与“通知生成”分离开。由一个专用的生成器Generator以可控的步长逐步Progressively创建通知任务并分批打入队列避免一次性淹没队列。优化与运维保障1. 批量分发Batching对比如果 1 次 API 调用仅发送 1 条通知100 万条通知就需要 100 万次网络 I/O。优化大部分主流供应商提供 Batch API。通过将 500 条通知合并为一个 Batch 请求发送100 万条通知只需 2000 次请求极大降低了网络开销与上下文切换带来的损耗。2. 失败重试与死信队列DLQ由于设备 Token 失效、邮箱错误或临时网络抖动通知必然会出现失败。策略引入带有指数退避Exponential Backoff的重试队列。死信队列限制最大重试次数如 5 次。超过上限仍失败的消息投递到死信队列Dead Letter Queue由运维团队后续通过监控进行排查与异步修复。3. 异步事件驱动的数据分析Delivery Tracking市场部通常需要追踪“已发送、已送达、已打开、失败”等指标。设计通知供应商的回调Callbacks通过异步事件进行处理。分析系统的流量与通知发送的流量相互独立避免数据分析压力反噬发送链路。总结一个生产级别的海量通知系统核心在于控制力用队列控制对内部应用层的冲击。用限流控制对外部下游的冲击。用幂等解决分布式架构的副作用。用批处理与异步事件榨干系统性能。