cswork / 系统设计

第 36 章 / 共 57 章 · 约 4 分钟

设计模式:实时更新

本页目录
  1. 1. 最后一公里:四种方式
  2. 2. 服务端内部:事件怎么找到连接
  3. 3. 不丢、不乱:可靠性要另外设计
  4. 4. 数量级
  5. 5. 常见追问
  6. 6. 本站用到这个模式的案例
  7. 章节测验

很多产品需要服务器主动把变化推给客户端:新消息、司机的位置、抢票队列的进度、文档里别人的光标。HTTP 默认是客户端问、服务器答,所以“实时更新”其实包含两个问题:最后一公里怎么把数据送到客户端,以及服务端内部怎么把一个事件送到持有那个用户连接的那台服务器。

1. 最后一公里:四种方式

方式原理适合代价
短轮询客户端每隔几秒请求一次更新不频繁、几秒延迟可以接受大量空请求;延迟取决于轮询间隔
长轮询请求挂起,直到有新数据或超时再返回中低频更新,不想改基础设施每次返回后要重新建请求;服务器要挂住很多请求
SSE(Server-Sent Events)一个 HTTP 长连接,服务器单向持续推送只需服务器推送:通知、进度、行情、AI 流式输出只能单向;客户端发消息仍走普通请求
WebSocket升级后的全双工长连接双向高频:聊天、协同编辑、游戏、位置共享有状态的连接,负载均衡、扩缩容、重连都更复杂

选择的思路:能轮询就先轮询;只需要服务器推送就用 SSE;双向高频才上 WebSocket。 WebRTC 用于点对点音视频,一般系统设计题用不到。

2. 服务端内部:事件怎么找到连接

用户的长连接落在某一台网关服务器上。业务服务产生事件时,并不知道这个用户连在哪台网关。

  • 连接注册表:网关在用户连上时写入 user_id → gateway_id(放在 Redis 里,带 TTL 并由心跳续期),业务服务查到网关后直接 RPC 投递。
  • 按网关的发布订阅:每台网关订阅自己的频道,业务服务把消息发到“该用户所在网关”的频道。
  • 按主题的发布订阅:每个房间或会话一个频道,订阅了这个频道的网关把消息转给本机上的相关连接。适合群聊、直播弹幕这类一对多场景。
  • 一致性哈希固定分配:按 user_id 把用户固定分到某台网关,查表这一步就省了,但扩缩容时会有一批用户需要重连。
实时更新的服务端投递路径
滚动到此处后加载图表…
图表可横向滚动,展开后可缩放查看。
查看图表源码
flowchart LR
  accTitle: 实时更新的服务端投递路径
  svc["业务服务产生事件"] --> reg[("连接注册表:user → 网关")]
  svc --> bus["发布订阅 / RPC"]
  bus --> g1["网关 1"]
  bus --> g2["网关 2"]
  g1 -->|"WebSocket / SSE"| c1["用户 A 的客户端"]
  g2 -->|"WebSocket / SSE"| c2["用户 B 的客户端"]
  svc --> db[("持久存储:带序号的事件")]
  c1 -.->|"重连后按序号补拉"| db

3. 不丢、不乱:可靠性要另外设计

  • 推送只是加速通道,不是唯一来源:客户端断线期间的事件不会自动补上。每个事件写入持久存储并带上单调递增的序号;客户端记住收到的最后一个序号,重连后按序号补拉缺失部分。
  • 推信号还是推数据:数据大或者变化频繁时,只推一个“有更新”的信号,让客户端自己去拉最新状态;消息本身就是内容时(聊天)才推完整数据。
  • 移动端后台:App 进入后台后长连接会被系统断开,要退回到 APNs / FCM 推送通知。
  • 重连风暴:一台网关重启,上万个客户端同时重连会打垮其他网关。客户端重连要带指数退避和随机抖动。

4. 数量级

  • 一台调优过的服务器可以维持几万到几十万个空闲长连接,瓶颈通常是内存和文件描述符,而不是 CPU。
  • 位置共享这类高频更新:每 4 秒一次、几百万在线,就是每秒几十万次写入,要按区域分片并在内存里处理。
  • 负载均衡要支持长连接(L7 的 WebSocket 支持,或 L4),空闲超时要大于心跳间隔。

5. 常见追问

  • 为什么不用短轮询? 几百万在线、每 2 秒轮询一次,就是每秒上百万次请求,大部分是空的。
  • WebSocket 服务器扩容或发布时怎么办? 先停止接新连接、等待现有连接自然迁移,或者通知客户端带退避重连;客户端靠序号补拉,不丢消息。
  • 一条消息要推给 100 万个订阅者(直播间)? 按房间分发到订阅了它的网关,每台网关只收一份,再本地扇出;必要时抽样或合并(每秒最多推 N 条弹幕)。
  • 怎么知道用户在不在线? 心跳更新带 TTL 的在线状态;好友的在线状态变化可以批量、延迟推送,不必实时。

6. 本站用到这个模式的案例

章节测验

5 道选择题
  1. 一个后台任务进度页只需要服务器把进度推给浏览器,浏览器不需要实时发消息。最合适的是?
  2. 业务服务要给用户 42 推一条消息,但用户 42 的 WebSocket 连在几百台网关中的某一台上。常见做法是?
  3. 用户的手机切到地铁隧道里断线 30 秒,期间有 5 条新消息。重连后怎样保证不丢?
  4. 一台 WebSocket 网关崩溃重启,上面的 5 万个客户端同时重连,压垮了其他网关。客户端应该怎么改?
  5. 信息流页面想提示“有新内容”,帖子数据很大,更新也很频繁。推送什么最合适?

章节讨论

正在加载讨论…