第 10 章 设计通知系统
简介
通知系统(Notification System)对现代应用至关重要,它能及时推送各类更新,例如产品通知、活动、优惠和提醒。通知可以通过以下方式发送:
- 推送通知(Push notifications)(移动端或桌面端),
- 短信(SMS messages),以及
- 电子邮件(Emails)。
本章重点设计一个可扩展的系统,能够每天发送数百万条通知。
第 1 步:理解问题
需求
- 通知类型: 推送通知、短信和电子邮件。
- 送达: 软实时(Soft real-time)系统,延迟尽可能小。
- 平台: iOS、Android 和桌面端。
- 触发方式: 通知可以由客户端应用触发,也可以在服务端定时发送。
- 规模:
- 推送通知: 每天 1000 万条,
- 短信: 每天 100 万条,
- 电子邮件: 每天 500 万条。
- 支持退订(Opt-out): 用户可以关闭特定类型的通知。
第 2 步:高层设计
组件
-
通知类型:
- iOS 推送通知: 使用 Apple 推送通知服务(Apple Push Notification Service,APNS)。
- Android 推送通知: 使用 Firebase 云消息传递(Firebase Cloud Messaging,FCM)。
- 短信: 使用 Twilio 或 Nexmo 等第三方服务。
- 电子邮件: 使用 SendGrid 或 Mailchimp 等商业邮件服务。
-
联系信息收集:
- 在应用安装或用户注册时收集设备令牌(device token)、电话号码或电子邮件地址。
- 将联系信息存储在数据库中:
- 设备令牌表: 用于推送通知。
- 用户表: 用于电子邮件和电话号码。
-
通知发送流程:
- 触发服务(Trigger Services):
- 生成事件以发起通知(例如账单提醒、物流更新)。
- 一个服务可以是一个微服务、一个定时任务(cron job),或者一个触发通知发送事件的分布式系统。
- 通知服务器(Notification Server):
- 为各服务提供发送通知的 API。
- 进行基本校验,验证电子邮件和电话号码。
- 查询数据库或缓存,获取渲染通知所需的数据。
- 第三方服务: 将通知送达用户。
- 触发服务(Trigger Services):
初始设计中的问题
- 单点故障(Single Point of Failure,SPOF): 一台通知服务器宕机就可能导致整个系统崩溃。
- 扩展性问题: 难以对数据库、缓存和处理组件分别进行独立扩展。
- 性能瓶颈: 发送通知需要消耗大量资源。
改进后的设计
- 将数据库和缓存移出通知服务器。
- 引入水平扩展(horizontal scaling),部署多台通知服务器。
- 使用消息队列(message queue)解耦系统组件。
- 当需要发送大量通知时,消息队列充当缓冲区。
- 增加工作节点(worker),从消息队列中拉取通知事件,并将其发送给相应的第三方服务。
第 3 步:深入设计
可靠性
-
防止数据丢失:
- 将通知数据持久化到数据库中,并实现重试机制。
- 引入通知日志数据库来实现数据持久化。
-
去重(Deduplication):
- 检查事件 ID,避免发送重复通知。
- 当一个通知事件首次到达时,通过检查事件 ID 判断之前是否见过它。 如果见过则丢弃,否则发送该通知。
其他组件
- 通知模板: 预先格式化的模板,使通知保持一致并提高效率。
- 通知设置:
- 用户可以选择订阅或退订特定渠道(推送、短信或电子邮件)。
- 存储在专门的通知设置表中。
- 限流(Rate Limiting): 限制向用户发送通知的频率。
- 重试机制: 当第三方服务失败时,重新发送通知。
- 监控队列: 追踪排队中的通知数量,以动态扩缩工作节点。
- 事件追踪: 收集打开率、点击率和参与度等指标。
安全
- 使用 AppKey 和 AppSecret 对推送通知的 API 进行认证和保护。
通知流程
- 触发服务调用 API 发送通知。
- 通知服务器校验请求,并从缓存或数据库中获取元数据。
- 通知事件被发送到消息队列。
- 工作节点处理事件,并与第三方服务交互。
- 第三方服务将通知送达用户。
关键优化
- 水平扩展: 增加通知服务器以分摊负载。
- 消息队列: 解耦处理流程,以应对大流量。
- 缓存: 缓存频繁访问的数据以降低延迟。
- 分布式抓取: 在地理上优化消息投递,以获得更好的性能。