通知 / 推送系统 架构模板
代表产品 / 原型:Novu、Courier、各类「消息中心 / 推送平台」,底层依赖 FCM / APNs 一句话定位:把「一件事发生了」可靠地、按用户偏好、通过多种渠道(站内 / 推送 / 短信 / 邮件 / IM)送达对的人——不重复、不轰炸、失败能重试。
1. 一句话定位
通知系统 = 一条「事件 → 触达」的统一管道。
它把系统里发生的各种事(订单发货了、有人 @ 你了、登录异常),变成用户在合适渠道、合适时机收到的一条通知。它是 事件驱动 + 消息队列 + 渠道适配器的综合体——核心难点是扇出(一件事通知海量人)、渠道抽象(各渠道天差地别)、和「不打扰」。
2. 业务本质:它在解决什么问题
通知是产品拉回用户、传递关键信息的命脉:交易状态、安全告警、社交互动、营销召回。一条及时的「你的快递到了」提升体验,一条「检测到异地登录」可能阻止盗号。
但它是把双刃剑:通知过度 = 骚扰 = 关推送 = 卸载。 所以这个系统的高级之处,不在「能发」,而在「克制地发」——发对的人、对的事、对的频率。
3. 核心需求与约束
功能性需求:
- [ ] 多渠道:站内信、App 推送、短信、邮件、IM(如企业微信 / Slack)
- [ ] 模板与多语言渲染
- [ ] 用户偏好与订阅(收什么、不收什么、免打扰时段)
- [ ] 去重、限频、聚合(把多条合并成一条摘要)
- [ ] 重试、定时 / 批量发送、已读状态
非功能性需求 / 质量属性:
| 质量属性 | 目标 | 为什么对这类系统重要 |
|---|---|---|
| 可靠投递 | 重要通知不丢 | 「交易 / 安全」类通知丢了是事故 |
| 及时性 | 秒级~分钟级 | @ 提醒、验证码晚了就没用 |
| 扇出能力 | 一事件→海量接收者 | 营销 / 公告可能推给上亿人 |
| 不打扰 | 限频 / 聚合 | 轰炸用户 = 用户流失 |
关键约束(不可逾越的边界):
- 🔴 各渠道协议 / 限制天差地别:App 推送必须经 APNs(iOS)/ FCM(Android),短信经运营商网关,各有速率与格式限制。
- 🔴 第三方渠道不可靠:推送 / 短信网关会延迟、会失败,必须重试 + 状态追踪。
- 🔴 营销洪峰:大促一条公告瞬间扇出上亿条。
- 🔴 合规:必须支持退订、免打扰、反垃圾(否则违法且伤品牌)。
4. 架构全景图
事件源(订单/社交/安全/营销…)
│ 「发生了 X」
▼
┌──────────────────────────────────────────────────────────────┐
│ 通知服务 │
│ ① 查用户偏好 / 订阅(他想不想收?现在是免打扰时段吗?) │
│ ② 模板渲染(填充内容、多语言) │
│ ③ 去重 + 限频 + 聚合(别重复、别轰炸) │
│ ④ 决定走哪些渠道、按优先级 │
└───────────────────────────┬──────────────────────────────────┘
▼ 入队(异步,削峰 + 重试)
┌──────────────────────┐
│ 消息队列(分优先级) │ 交易类 > 营销类
└──────────┬───────────┘
▼
┌────────────────────────────────────────────┐
│ 渠道适配器(把统一指令翻译成各渠道协议) │
└──┬─────────┬──────────┬──────────┬──────────┘
▼ ▼ ▼ ▼
┌─────┐ ┌──────┐ ┌───────┐ ┌──────┐
│站内 │ │ Push │ │ 短信 │ │ 邮件 │ → 用户
│ 信 │ │FCM/ │ │ 网关 │ │ │
│ │ │APNs │ │ │ │ │
└─────┘ └──────┘ └───────┘ └──────┘
失败 ──▶ 重试;投递状态回执 ──▶ 状态追踪灵魂在于:第 ①③ 步(偏好 + 去重限频)是「克制」,队列是「削峰 + 可靠」,渠道适配器是「屏蔽异构」。 一个只会「来一条发一条」的通知系统,迟早把用户轰走。
5. 组件职责
- 事件接入:接收各业务系统抛来的事件。为什么需要:解耦——业务只管喊「发生了 X」,不关心怎么通知。
- 偏好 / 订阅管理:记录每个用户想收什么、免打扰时段。为什么需要:尊重用户选择是体验和合规的底线。
- 模板引擎:把事件 + 数据渲染成各渠道的文案。为什么需要:内容要按渠道(短信 70 字 vs 邮件富文本)和语言定制。
- 去重 / 限频 / 聚合:防重复、防轰炸,把多条合并成摘要。为什么需要:这是「不打扰」的技术实现。
- 队列(分优先级):削峰 + 异步 + 重试缓冲。为什么需要:营销洪峰要削峰,且发通知绝不能阻塞业务(见决策 1)。
- 渠道适配器:把统一指令翻译成 APNs / FCM / 短信 / 邮件协议。为什么需要:屏蔽各渠道差异,和 AI 网关 的适配器同理。
- 投递状态追踪:记录每条通知发没发出、成没成功。为什么需要:可靠投递和可观测的基础。
6. 关键数据流
场景一:一条通知的旅程
1. 事件「订单已发货(用户 U)」 ──▶ 通知服务
2. 查 U 的偏好:他开了「物流通知」,且现在不是免打扰 ──▶ 继续
3. 渲染模板:「您的订单已发货,预计明天送达」
4. 去重检查:这条没发过 ──▶ 入队(优先级:交易类=高)
5. 渠道适配器:U 装了 App ──▶ 走 FCM 推送;同时落一条站内信
6. FCM 返回成功 ──▶ 记录投递状态;失败则重试 / 降级到短信场景二:限频聚合(防轰炸)
某帖子 1 分钟内被 50 人点赞:
✗ 笨做法:推 50 条「XX 赞了你」──▶ 用户被轰炸,关推送
✓ 聚合:攒一个时间窗,合并成「50 人赞了你的帖子」一条
── 技术能力要服务于「不打扰」的产品智慧7. 数据模型与存储选择
核心实体:通知;用户偏好 / 订阅;模板;投递记录;站内消息。
| 数据 | 存储类型 | 为什么 |
|---|---|---|
| 用户偏好 / 订阅 | 关系型 | 结构化、要一致、频繁查 |
| 站内信箱 | 见 社交信息流 的收件箱模型 | 写扩散 / 读扩散的取舍 |
| 投递记录 / 状态 | 时序 / 列存 | 海量、按时间聚合统计投递率 |
| 待发队列 | 消息队列 | 削峰、重试、优先级 |
8. 关键架构决策与权衡 ⭐
决策 1:同步发,还是异步发?⭐
- 同步:业务调通知接口,等它发完才继续。简单,但通知渠道一慢 / 一挂,业务主流程就被拖死。
- 异步:业务只把事件丢进队列,立刻返回;通知服务慢慢发。
- 取向:必须异步。发通知是非关键路径,绝不能阻塞业务,且队列天然给了削峰和重试。
决策 2:去重与限频(不打扰的核心)⭐
- 不做:同一事件可能触发多条、热点事件疯狂轰炸。
- 做:幂等去重(同一通知只发一次)+ 限频(单位时间上限)+ 聚合(合并成摘要)。
- 取向:必做。「不打扰」是产品生命线,技术要为它服务。
决策 3:推送渠道——自建还是用平台?
- 自己连设备:不可能,iOS / Android 推送必须走 APNs / FCM。
- 取向:App 推送一律经 APNs / FCM;系统只负责「调好这些平台」,不可能绕过。
决策 4:投递保证——at-least-once + 幂等
- 重要通知宁可重发也别丢(at-least-once),但重发会导致用户收到重复。
- 取向:服务端 at-least-once 重试,客户端 / 渠道侧做幂等去重。和 支付 一样:宁重勿丢,靠幂等兜重复。
9. 规模化与瓶颈
- 第一个瓶颈:营销扇出洪峰(亿级)。 → 破解:队列削峰 + 批量发送 + 按用户分片并行。
- 第二个瓶颈:渠道限流(短信 / 推送有速率上限)。 → 破解:按渠道配额排队、平滑发送。
- 第三个瓶颈:交易通知被营销洪峰挤压。 → 破解:优先级队列,交易 / 安全类走高优先级独立通道。
- 第四个瓶颈:投递状态数据海量。 → 破解:时序存储 + 聚合统计投递率。
10. 安全与合规要点
- 退订与免打扰:必须支持一键退订、免打扰时段,满足反垃圾 / 隐私法规(否则违法)。
- 防轰炸 / 防滥用:限频既是体验也是安全(防止被用来骚扰)。
- 内容安全:模板要防注入(用户数据填进模板别被利用);通知内容可能含敏感信息,要脱敏。
- 渠道凭证安全:APNs 证书 / 短信网关密钥是敏感凭证。
11. 常见误区 / 反模式
- ❌ 同步发通知,阻塞业务主流程 → ✅ 异步队列,通知是非关键路径。
- ❌ 不去重、不限频,疯狂轰炸 → ✅ 去重 + 限频 + 聚合摘要。
- ❌ 交易通知和营销混一个优先级 → ✅ 分优先级 / 分通道,重要的先到。
- ❌ 无视用户偏好,硬发 → ✅ 偏好 + 退订 + 免打扰。
- ❌ 一条一条发,不批量 → ✅ 大扇出要批量。
- ❌ 假设渠道一定成功 → ✅ 重试 + 投递状态追踪 + 降级。
12. 演进路线:MVP → 成长期 → 成熟期(不同阶段怎么设置)
| 阶段 | 规模量级 | 怎么设置(具体) | 此时该操心什么 |
|---|---|---|---|
| MVP | 起步 | 一两个渠道(邮件 / Push),直接接 FCM / 邮件服务,简单队列异步发 | 先把「该通知的事能通知到」跑通 |
| 成长期 | 多渠道 / 上量 | 用 Novu 类或自建:多渠道 + 模板 + 偏好 + 去重限频 + 异步队列 + 重试 | 可靠投递、不打扰、扇出能力 |
| 成熟期 | 亿级扇出 | 优先级队列、智能聚合(摘要)、跨渠道编排(先 Push 没读再短信)、A/B、投递率可观测 | 规模、优先级保障、转化、合规 |
13. 可复用要点
- 💡 异步队列削峰 + 重试,是处理「非关键路径」的标准姿势。 发通知、发邮件、写日志,都不该挡在主流程上。
- 💡 渠道适配器屏蔽异构第三方:对内一套指令,对外 N 个适配器——和 AI 网关「适配多供应商」是同一招。
- 💡 at-least-once + 幂等去重:宁可重发也别丢,靠去重兜住重复,呼应 支付系统。
- 💡 「不打扰」是产品智慧高于技术能力的范例。 能发不代表该发——好系统懂得克制。
🎯 随堂检验
- A同步发送,等发完再继续业务
- B异步入队 + 去重限频,绝不阻塞业务主流程
- C来一条事件就立刻发一条
参考原型与延伸阅读
本模板基于以下真实开源项目与官方文档整理。
🔧 开源原型(可直接读代码):
- novuhq/novu — 开源通知基础设施,统一 API 覆盖站内 / 邮件 / 短信 / Push / Slack 多渠道,体现多渠道扇出、模板、工作流编排。
📖 官方文档:
- Firebase Cloud Messaging 架构总览 — Google 官方文档,讲 FCM 后端的主题扇出、传输层路由与投递,推送系统的权威参考。
- FCM 通过 APNs 投递 iOS 推送 — 说明 FCM 如何以 Apple APNs 作为传输层,体现「多平台传输层对接」。
📌 一句话记住通知系统:它不是「一个能发短信的接口」,而是「一条把『发生了什么』克制地、可靠地、按偏好送达对的人的管道」——所有设计都在回答『怎么发对人、发对事、发对频率,还不丢、不重、不轰炸』。