设计一个包含移动端,短信,邮件的通知推送系统。
Step 1 - 明确需求
以下面的需求作为设计目标:
- 支持哪些类型的推送?=> APP推送,短信,邮件
- 需要实时推送吗?=> 尽量实时,如果系统负载压力大,则允许一定的延时
- 支持哪些设备?=> iOS设备,安卓设备,桌面端
- 如何触发推送?=> 可以由客户端触发,也可以由服务端主动发起推送
- 用户可以退订推送吗?=> 可以
- 每天推送多少条通知?=> 1000万APP推送,1百万短信,500万邮件
Step 2 - 概要设计
包含以下三个方面:
不同的推送系统
iOS推送

需要借助APNS(Apple Push Notification Service)进行推送,推送流程如下:
- 发起推送方需要获取设备Token并构造Payload
- 发送方将Token和Payload发送给APNS,由APNS将推送传递给客户端
- 客户端收到推送
安卓推送
与iOS类似,但不使用APNS,而是使用FCM(Firebase Cloud Messaging)进行转发。

短信推送
使用第三方短信服务提供商进行推送。

邮件推送
使用商业邮件服务提供商进行推送。

推送方式总结

联系方式信息收集
需要移动端设备标记,手机号码,邮件地址等信息,通常是当用户安装注册APP时提供,API服务器需要收集这些信息并保存在数据库中。

推送流程
概要设计

服务1~N:可以是微服务,也可以是定时任务,也可以是分布式系统触发的事件导致的推送。
通知系统:整个系统的中心,用于收发推送,提供API接口给服务1~N,并且调用第三方的服务发送推送消息。
第三方服务:提供iOS推送,安卓推送,短信推送,邮件推送等功能。
上面的设计包含以下问题:
- 单点故障:通知系统包含单点故障隐患。
- 不容易扩展:通知系统不利于扩展,比如增加数据库,缓存,以及不同的推送组件。
- 性能瓶颈:通知可能很消耗资源,单一的推送系统容易导致系统过载。
概要设计改进

改进点:
- 将数据库和缓存独立出来,放到推送系统外面
- 增加更多的推送服务器以应对单点故障
- 引入消息队列,解耦合系统组件
Step 3 - 详细设计
讨论以下问题的处理:
- 可靠性
- 增加额外组件,比如推送模版,推送设置,限流,重试机制,安全问题,系统监控与事件跟踪
- 升级设计
可靠性
如何防止数据丢失?
对于推送系统而言,消息可以重复或延时,但不能丢失。为了满足这个需求,推送系统需要在数据库中维护推送日志相关的数据,并且实现重试机制。

接收者需要精确保证只接收一遍推送吗?
不需要。分布式环境下本来就有可能产生重复推送,为了降低重复推送的概率,我们引入一个重复数据删除机制:当一条推送到达时,先检查事件ID,看是否已经推送过,如果是,则丢弃这条消息,否则,发送该推送。
额外组件及考虑因素
推送模版
大部分的推送消息格式基本一致,只需要修改诸如联系人名称之类的信息,所以可以引入推送模版,通过模版替换的方式来降低构造消息的难度,以及保持消息格式的一致性。
推送设置
提供精细化的用户设置,以方便用户调整推送设置,比如是否推送,推送间隔等。
限流
控制推送频率。
重试机制
当第三方推送返回失败时,通过消息队列记录该消息,以便于在合适的时间点重新推送,如果问题仍然存在,则需要发出警告给系统维护者。
安全问题
对于iOS和安卓应用,使用appKey和appSecret来保证推送的安全。只能通过认证的用户都允许通过提供的API来发送推送。
监控排队等待发送的消息
消息队列中不能积压太多的消息,如果积压了太多的消息,则表示工作服务器处理能力不够。为了提升推送的传达速度,需要增加更多的工作服务器。
事件追踪
推送数据,比如打开率,点击率,参与率,对于分析客户的行为非常重要。数据分析依赖于事件追踪,通常需要整合推送系统与数据分析。
升级设计

改进点:
- 推送服务器增加了两项关键特征:认证及限流。
- 增加了重试机制,以应对推送失败的情况。如果推送失败,则将消息重新压入消息队列,工作服务器会重复取出并发尝试发送出去,直到重复次数超过预设值。
- 增加了推送模版,简化消息生成流程,保持消息格式的一致性。
- 增加了监控与追踪系统,用于判断系统运行状态,以及用于将来集成新特性。
Step 4 - 总结
关键点:
- 使用消息队列解耦合各个组件
- 应用重试机制降低推送失败率
- 使用AppKey/appSecret进行认证,只有受认证的客户端才可以发送推送
- 增加追踪与监控
- 支持退订,推送消息之前先检查用户设置
- 限流