完整架构

三个月后,通知系统长成了这样一张完整的架构图。它不再是某个脚本,而是由业务层、通知服务层、数据层和监控层组成的平台。

分层架构

┌─────────────────────────────────────────────────────────────────┐
│                          业务层                                 │
│  ┌──────────┐  ┌──────────┐  ┌──────────┐  ┌──────────┐      │
│  │ 评论服务 │  │ 点赞服务 │  │ 关注服务 │  │ 私信服务 │      │
│  └─────┬────┘  └─────┬────┘  └─────┬────┘  └─────┬────┘      │
│        └──────────────┴──────────────┴──────────────┘            │
└──────────────────────────────┼───────────────────────────────────┘

┌─────────────────────────────────────────────────────────────────┐
│                        通知服务层                               │
│  ┌──────────────────────────────────────────────────────────┐  │
│  │                   通知调度服务                            │  │
│  │  - 接收业务事件 / 检查用户偏好 / 限流检查 / 消息聚合     │  │
│  └───────────────────────┬──────────────────────────────────┘  │
│              ┌───────────┴───────────┐                         │
│              ▼                       ▼                         │
│  ┌──────────────────┐    ┌──────────────────┐                │
│  │   推送服务       │    │   站内信服务     │                │
│  │  - APNs/厂商推送 │    │  - 消息存储/查询 │                │
│  └──────────────────┘    └──────────────────┘                │
└─────────────────────────────────────────────────────────────────┘

┌─────────────────────────────────────────────────────────────────┐
│  ┌──────────┐  ┌──────────┐  ┌──────────┐  ┌──────────┐      │
│  │  MySQL   │  │  Redis   │  │ RabbitMQ │  │  ES      │      │
│  │ 消息存储 │  │ 缓存/限流│  │ 消息队列 │  │ 日志搜索 │      │
│  └──────────┘  └──────────┘  └──────────┘  └──────────┘      │
└─────────────────────────────────────────────────────────────────┘

各层职责

业务层

业务服务(评论、点赞、关注、私信)只做一件事:把”发生了什么”作为事件发出去,不关心通知怎么投递。这样业务逻辑和通知逻辑彻底解耦,新增业务只需要接入事件,不需要改动通知系统。

通知服务层

这一层是通知系统的核心,分成三块:

  • 通知调度服务:接收业务事件,依次完成偏好检查、限流检查、消息聚合,然后决定是走推送还是站内信(或两者)。
  • 推送服务:对接 APNs 和各安卓厂商推送,负责 token 管理、失败重试、静默推送和到达率监控。
  • 站内信服务:负责消息持久化、已读/未读状态、多端同步和消息中心查询。

数据层

  • MySQL(分表):站内信按用户 ID 分 16 张表,配合未读计数表和用户设备表。
  • Redis:用户偏好缓存、未读计数、推送限流计数和分布式锁。
  • RabbitMQ:异步削峰,把推送和站内信写入从业务链路中解耦出来。
  • Elasticsearch:通知日志的搜索和分析。

监控层

Prometheus + Grafana 采集推送延迟、成功率、重复率、投诉率等指标,异常时触发告警。

一条通知的完整旅程

用户 A 评论了用户 B 的动态

评论服务发出事件:comment.created

RabbitMQ 接收事件(削峰)

通知调度服务:
  1. 查用户 B 的偏好(Redis)——是否接收评论通知?
  2. 限流检查(Redis)——同一动态 1 小时内是否已发过?
  3. 消息聚合——同一用户连续评论合并?

命中投递策略:
  站内信写入 MySQL 分表(记录已读/未读)
  推送走 APNs/厂商推送(失败自动重试 3 次)

用户 B 的 iPhone 收到推送,打开 App 看到站内信
其他设备通过静默推送同步未读状态

高可用设计

  • 服务降级:推送服务不可用时,只发站内信,保证消息不丢。
  • 消息重试:推送失败自动重试 3 次,配合死信队列兜底。
  • 幂等处理:通知 ID + 消费记录,防止消息队列重复消费导致重复推送。
  • 监控告警:延迟、成功率、投诉率超过阈值自动告警。

关键指标

指标优化前优化后
推送延迟5-10 秒< 1 秒
推送成功率85%95%
重复推送率5%< 0.1%
系统可用性95%99.9%

这套架构的关键不在于用了多少中间件,而在于每一层都只解决一类问题:业务层产生事实,调度层做决策,投递层保证触达,数据层负责存储,监控层让一切可观测。