system-design-notes:无状态聊天架构为什么更优?ZooKeeper服务发现全解
【免费下载链接】system-design-notesNotes of the book System Desgin Interview - An Insider's Guide项目地址: https://gitcode.com/GitHub_Trending/sy/system-design-notes
system-design-notes 是《System Design Interview — An Insider's Guide》的系统设计笔记仓库。本文基于仓库第 12 章「Design a Chat System」,讲透两件事:无状态聊天架构为什么比有状态架构更优,以及ZooKeeper 服务发现如何帮用户挑到最佳聊天服务器。
💡 核心关键词:无状态聊天架构、聊天系统设计、ZooKeeper 服务发现、WebSocket 长连接
一、聊天系统:为什么"状态"是最大的难题
先看看第 12 章给定的规模目标:
- 📱5000 万日活用户,聊天历史永久存储
- 💬 一对一聊天 + 群组聊天(每群最多 100 人)
- 🟢 在线/离线状态指示、多设备支持、推送通知
网页类应用(登录、查资料)天然是无状态的:每个 HTTP 请求独立,服务器可以随时扩容缩容。但聊天应用不同——用户要和服务器保持一条WebSocket 长连接,连接本身就"绑定"在某台服务器上。这就引出两种架构选择。
二、有状态 vs 无状态:聊天架构两种方案对比

有状态架构:所有用户直接连到一台台聊天服务上,连接状态(在线用户、会话上下文)留在各服务器内存里。问题很明显:
- 某台聊天服务器宕机,它上面的所有用户掉线,且无法把连接"搬"到别处
- 扩容/缩容受连接分布限制,难以做到真正的负载均衡
- 多设备登录时,同一用户在不同服务器上状态不一致,同步困难

无状态架构则把"能无状态的部分"全部无状态化:
| 对比维度 | 有状态架构 | 无状态架构 ✅ |
|---|---|---|
| 登录/资料/认证 | 分散在各聊天服务器 | 集中在无状态 API 层,任意服务器可处理 |
| 水平扩容 | 受连接分布限制 | 负载均衡器直接分发,加机器即可 |
| 服务器宕机 | 连接丢失、用户掉线 | 服务发现重新分配,用户可快速重连 |
| 多设备支持 | 状态不一致 | 状态统一放共享存储,按消息 ID 同步 |
| 推送通知 | 各服务器自行处理 | 独立通知服务器 + 第三方推送服务 |
核心思想一句话:把"状态"从服务器里搬出去——搬到共享存储(KV Store)和专门的状态服务器(聊天服务器只负责维持长连接)。这正是第 1 章"Stateless Web Tier"思想的延续:01. Scaling/Readme.md 中提到,会话数据移入共享存储后,Web 服务器即可自由水平扩缩。
三、无状态聊天架构的核心组件

客户端通过WebSocket(ws 协议)与聊天服务器维持双向持久连接,这是实时收发消息的基石。整体组件如下:
- API 服务器(无状态):处理登录、注册、改资料等一切 HTTP 请求
- 聊天服务器(有状态长连接):只负责维持 WebSocket 连接、转发消息
- 在线状态服务器:管理用户在线/离线状态
- 通知服务器:对接第三方推送服务(如 APNs/FCM)
- KV Store:永久存储聊天历史——它支持水平扩展、延迟极低,且 Facebook Messenger、Discord 等成熟聊天产品都采用 KV 存储
四、ZooKeeper 服务发现全解:用户如何找到最佳聊天服务器
无状态架构带来一个新问题:用户登录拿到的是 API 服务器,但聊天服务器到底连哪一台?如果连到负载已满或地理位置很远的服务器,消息延迟会明显变差。

仓库方案采用Apache ZooKeeper作为服务发现组件,完整流程只有 4 步:
| 步骤 | 动作 | 说明 |
|---|---|---|
| ① | 用户登录 | 请求先到负载均衡器,转发到 API 服务器 |
| ② | API 服务器处理登录 | 验证身份,准备建立长连接 |
| ③ | 查询服务发现 | 询问 ZooKeeper:"给这个用户推荐一台聊天服务器" |
| ④ | 建立 WebSocket 连接 | 用户直连被推荐的 Chat Server,开始聊天 |
ZooKeeper 推荐时综合两个维度:
- 🌍地理位置:优先分配同区域的聊天服务器,压低消息延迟
- ⚖️服务器容量:优先分配负载较轻的机器,避免热点
更重要的是容错:当某台聊天服务器宕机时,ZooKeeper 感知到节点下线并把它从可用列表移除,掉线用户重连时会自动被分配到新服务器——这就是无状态架构"服务器故障不丢连接语义"的关键一环。
五、消息流动:消息 ID 如何保证顺序一致

- 一对一聊天:用户 A 发消息 → 聊天服务器 1 生成全局唯一消息 ID并存入 KV Store → 若用户 B 在线,转发到其所在聊天服务器;离线则走推送通知
- 群组聊天:消息复制到群内每个成员的收件箱(消息同步队列)。群组上限 100 人,复制成本可控,还天然简化了同步逻辑
消息 ID 是顺序的灵魂:采用 Snowflake 这类全局序列生成器即可;更优做法是用局部序列号(仅在单个会话内唯一),因为聊天只需要保证"单个会话内的先后顺序"。

多设备同步则靠消息 ID 完成:每台设备记录本地已读的最大消息 ID(cur_max_message_id),新设备上线后只拉取 KV Store 中"接收者是自己且 ID 更大"的消息,即可追平所有终端,无需服务器逐台推送。
六、关键要点回顾 ✅
- 聊天系统 = 无状态 API 层 + 有状态长连接层 + 共享存储,状态外移是无状态架构更优的根本原因
- 无状态层让扩容、宕机恢复、多设备一致性都变得简单
- ZooKeeper 服务发现按地理位置 + 服务器容量推荐聊天服务器,并在故障时自动重新分配
- 消息 ID(局部序列即可)同时解决顺序、同步、多端一致三个问题
📎 延伸阅读(仓库内笔记)
- 聊天系统完整设计:12. Chat System/Readme.md
- 从单机到百万用户的扩展之路(含无状态 Web 层):01. Scaling/Readme.md
- 仓库总目录:Readme.md
【免费下载链接】system-design-notesNotes of the book System Desgin Interview - An Insider's Guide项目地址: https://gitcode.com/GitHub_Trending/sy/system-design-notes
创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考