通知系统:通知类型、聚合去重、多端同步与推送架构

全面讲解微型博客的通知系统:通知类型建模(赞/转/评/@/关注)、通知聚合与去重、拉取式与推送式通知架构、WebSocket/SSE/APNs/FCM 多通道推送、已读/未读状态与红点、多端同步与幂等,以及通知系统的可靠性与降级。

通知是微型博客的「唤醒系统」——它把「发生在你身上的事」主动送到用户眼前。但通知系统最大的挑战不是「发出去」,而是「发得对、发得巧、不重复、不丢」。

本文系统讲解:通知类型建模、聚合与去重、拉取/推送架构、多通道下发、已读状态与多端同步。

一、通知类型建模

1.1 常见通知类型

类型触发事件聚合性
点赞有人赞你的帖可聚合
转发有人转你的帖可聚合
回复有人回复你的帖/评论可聚合
@提及有人 @ 你弱聚合
关注有人关注你可聚合
系统平台消息不聚合

1.2 通知数据模型

CREATE TABLE notifications (
  id BIGINT PRIMARY KEY,
  user_id BIGINT NOT NULL,              -- 接收者
  type SMALLINT NOT NULL,               -- 1赞 2转 3评 4@ 5关注 6系统
  actor_id BIGINT,                      -- 最近一个触发者
  actor_ids BIGINT[],                   -- 聚合的全部触发者
  post_id BIGINT,                       -- 关联对象
  comment_id BIGINT,
  agg_count INT DEFAULT 1,              -- 聚合条数
  is_read BOOLEAN DEFAULT FALSE,
  created_at TIMESTAMPTZ DEFAULT now(),
  updated_at TIMESTAMPTZ DEFAULT now()
);
CREATE INDEX idx_notif_user ON notifications(user_id, is_read, created_at DESC);

1.3 通知入口

通知产生链路:
  事件(点赞/回复等)→ 事件总线 → 通知服务
  → 判断接收者是否在线/在设置中开启 → 生成通知 → 聚合/入队
  → 拉取通道入库 + 推送通道下发

一句话总结:通知建模的关键是「类型可扩展、聚合有字段支撑」——用 type 区分、actor_ids 支撑聚合、agg_count 记录折叠数。

二、聚合与去重

2.1 为什么需要聚合

不聚合的后果:张三、李四、王五 5 秒内各赞一次 → 用户收到 3 条通知,烦人且淹没真实信息。

聚合:同对象、同类型的多次互动合并成一条「张三等 5 人赞了你」。

2.2 聚合规则

聚合键 = (user_id, type, post_id)
时间窗 = 同键下 N 分钟内(如 30 分钟)新增 → 并入同一条
      超出时间窗 → 新开一条

实现:
  通知表 UNIQUE 约束(user_id, type, post_id, window_start)
  新事件 → INSERT ON CONFLICT 更新 agg_count + actor_ids

2.3 去重

- 幂等事件:同一事件重复投递(如消息队列重试)
  用事件唯一键(如 event_id)去重
- 用户自身操作:自己赞自己 → 不通知
- 已拉黑用户:不通知

2.4 聚合示例 SQL

-- 聚合写入(简化)
INSERT INTO notifications (user_id, type, actor_ids, post_id, agg_count)
VALUES (:uid, 1, ARRAY[:actor], :post, 1)
ON CONFLICT (user_id, type, post_id, window_start)
DO UPDATE SET
  actor_ids = notifications.actor_ids || EXCLUDED.actor_ids,
  agg_count = notifications.agg_count + 1,
  updated_at = now();

一句话总结:聚合把「N 条骚扰」折叠成「1 条信息」,用冲突更新原子完成并入——这是通知体验的核心细节。

三、拉取式与推送式

3.1 拉取式(Pull)

客户端轮询 / 刷新时拉取:
  GET /api/notifications?cursor=xxx
  优点:实现简单、无长连接
  缺点:不实时、有轮询开销
  适用:Web 端、低实时要求

3.2 推送式(Push)

服务端主动下发:
  - WebSocket / SSE(在线端,实时)
  - APNs / FCM(移动端,离线也到)
  - 站内信(App 内红点)
  优点:实时
  缺点:通道维护复杂

3.3 双通道设计

在线:WebSocket 推送 → 即时更新红点/列表
离线:APNs/FCM 推送 → 系统通知栏
兜底:下拉刷新拉取 → 保证不丢

顺序:WS 优先 → 失败/离线走 APNs/FCM → 客户端刷新兜底

一句话总结:通知下发是「多通道冗余」——实时走长连接、离线走厂商通道、兜底靠拉取,三层保证「要么即时看到、要么下次打开能拉回」。

四、多通道下发架构

4.1 架构图

事件流 → 通知服务
  ├─ 入库(PostgreSQL notifications 表)
  ├─ 在线推送(WebSocket 网关 / SSE)
  └─ 离线推送(APNs / FCM,经推送网关)

移动端:
  App 打开时:建立 WS → 实时
  App 关闭时:FCM/APNs → 通知栏
  App 打开但 WS 断:拉取兜底

4.2 推送通道管理

- 设备 token 注册表(user → device tokens)
- token 失效清理(APNs/FCM 返回 Unregistered)
- 推送频率限制(防骚扰)
- 静默通知 / 声音 / 横幅的偏好设置

4.3 WebSocket 消息

{
  "type": "notification",
  "data": {
    "id": 12345,
    "type": 1,
    "agg_count": 5,
    "summary": "张三等 5 人赞了你",
    "post_id": 888
  }
}

一句话总结:多通道下发 = 「入库保证可靠 + WS 保证在线实时 + 厂商通道保证离线触达」三件套,核心是设备注册表与 token 生命周期管理。

五、已读状态与红点

5.1 已读/未读

- 未读计数:SELECT count(*) FROM notifications WHERE user_id=? AND is_read=false
- 全部已读:UPDATE notifications SET is_read=true WHERE user_id=?
- 单条已读:UPDATE ... WHERE id=?

5.2 红点缓存

Redis:unread:{uid} → 未读数(读时 INCR,清时 SET 0)
  - 新通知 → INCR
  - 已读 → 客户端拉取真实计数或 SET 0
  - 保持「读后清零」与「服务端计数」最终一致

5.3 会话维度

通知可挂在「会话/私信」维度:
  - 未读私信数、未读群消息数
  - 与系统通知分开计数
  - 红点分 Tab 展示(互动/私信/系统)

一句话总结:已读/红点的核心是「缓存计数 + 最终一致」——未读数放 Redis 扛高频读,读后清零靠客户端回传与服务端校验收敛。

六、多端同步与幂等

6.1 多端同步

用户同时登录手机 + 桌面:
  同一通知在两个端都要显示、任一端已读则全局已读
  方案:
    - 通知 id 全局唯一 → 各端拉取同一条
    - 已读状态存服务端 → 任一端读后同步
    - WS 推送向用户的所有在线连接广播

6.2 幂等

- 通知生成幂等:同一事件不重复生成通知
  (消息队列 at-least-once → 需事件唯一键去重)
- 已读幂等:重复已读请求无害
- 推送幂等:同一通知可能从多个通道到达 → 客户端去重(按通知 id)

6.3 可靠性保障

- 通知表是「真相源」,推送只是加速展示
- 丢失推送 → 下次拉取总能补回(拉取兜底)
- 推送失败 → 记录日志 + 重试 + 降级为拉取

一句话总结:多端同步的关键是「服务端状态 + 客户端幂等」——已读、通知 id、推送都归一,任一端的操作都能全局收敛。

七、总结

微型博客通知系统的建设路径可以概括为:先建模、再聚合、双通道、终同步。通知类型用枚举建模、actor_ids 支撑聚合折叠;聚合规则把骚扰变成信息;拉取与推送双通道覆盖在线与离线;已读红点用缓存计数保高频;多端同步靠服务端状态与幂等收敛。

通知系统是微型博客的「神经末梢」——它不生产内容,却决定内容能否抵达用户。发得准、发得巧、不重复、不丢失,这四件事做对,通知系统就从「打扰」变成「价值」。

继续阅读

探索更多技术文章

浏览归档,发现更多关于系统设计、工具链和工程实践的内容。

全部文章 返回首页

「miniblog」更多文章

  1. 速率限制与防滥用:令牌桶、滑动窗口与分布式限流
  2. 评论与互动系统:评论树、@提及、点赞转发与互动计数一致性
  3. 用户鉴权与会话:密码哈希、JWT vs Session、OAuth2 与刷新令牌