Skip to content

通知 / 推送系统 架构模板

代表产品 / 原型: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 多渠道,体现多渠道扇出、模板、工作流编排。

📖 官方文档:


📌 一句话记住通知系统:它不是「一个能发短信的接口」,而是「一条把『发生了什么』克制地、可靠地、按偏好送达对的人的管道」——所有设计都在回答『怎么发对人、发对事、发对频率,还不丢、不重、不轰炸』。