☰
Facebook Messenger 系统设计实战:基于 Grokking-System-Design 从单服务器消息转发到分布式聊天集群的架构演进
2026/10/8 2:02:26 网站建设 项目流程
  • 教程

【免费下载链接】Grokking-System-Design

Systems design is the process of defining the architecture, modules, interfaces, and data for a system to satisfy specified requirements. Systems design could be seen as the application of systems theory to product development.

项目地址:https://gitcode.com/gh_mirrors/gr/Grokking-System-Design
点击查看免费下载

导读

本文基于 designs/facebook-messenger.md 展开,该文档以「概述图 + 细节图」两张架构图精炼地承载了 Facebook Messenger 类聊天系统的完整设计脉络:先给出用户经聊天服务器互发消息的基础链路,再给出负载均衡、聊天服务器集群、数据库分片与缓存集群组成的分布式扩展架构。读完本文,你将掌握即时消息系统从单点到集群的演进路径、图中每个组件的职责,以及仓库基础文档(负载均衡、分片、缓存、实时通信、队列、冗余、CAP 定理等)在聊天场景中的具体落点,可直接用于系统设计面试的讲解与复盘。


一、文档概览:两张图讲清一个完整设计

与仓库中大多数设计文档(如 designs/short-url.md 的需求清单、容量估算、API 表格、数据模型等长篇结构)不同,facebook-messenger.md采用极简的组织方式:正文仅一个## Summary小节,所有技术内容全部封装在两张架构图中——

  • 概述图(img/facebook-messenger-overview.png):展示最基础的聊天系统形态,回答"消息如何从一个人传到另一个人并落盘";
  • 细节图(img/facebook-messenger-detail.png):展示分布式扩展后的完整形态,回答"用户量大之后如何拆分与加速"。

这两张图恰好对应了系统设计面试中"先设计最小可行系统,再识别瓶颈、逐步扩展"的标准节奏(参见 README.md 的 Interview Process 一节)。


二、基础架构:单聊天服务器的消息流转

概述图描绘了一个最简单的点对点聊天拓扑,共 4 个组件、4 条消息流向:

组件职责(按图示)
User 1/User 2消息的发送方与接收方
Chat Server消息转发的核心枢纽
Data storage消息的持久化存储

数据流如下:

  1. User 1向Chat Server发送消息 A(Send Message A);
  2. Chat Server将消息 A 转发给User 2(Receive Message A);
  3. User 2向Chat Server发送消息 B(Send Message B);
  4. Chat Server将消息 B 转发给User 1(Receive Message B);
  5. 在此过程中,Chat Server与Data storage之间为双向读写,即消息既要落盘持久化,也要在必要时从存储中取回(例如离线消息、历史消息查询)。

2.1 架构含义解读

从这张图可以提炼出聊天系统的两个本质约束:

  • 双向实时通信:用户与聊天服务器之间不是一问一答的普通 HTTP 请求,而是需要服务器能主动向客户端推送消息。仓库的 basics/client-server-communication.md 列出了从标准 HTTP、Ajax 轮询、HTTP 长轮询、WebSocket 到 Server-Sent Events(SSE)的演进,其中WebSocket(基于单条 TCP 连接的全双工通道、低通信开销、实时数据传输)最贴合"发送 + 接收"对称的场景;
  • 消息必须持久化:Chat Server与Data storage的双向箭头表明,消息不是转瞬即逝的网络包,而是要落库的"数据对象",这决定了后续数据模型与存储选型(SQL/NoSQL、分片)的设计空间。

2.2 显而易见的瓶颈

基础架构只有一个Chat Server和一个Data storage,两者都是典型的单点故障与性能瓶颈:

  • 单台聊天服务器承载所有连接与转发,CPU、内存、文件描述符(每个长连接都会占用)很快耗尽;
  • 单一存储无法支撑海量消息的写入与读取;
  • 任一组件宕机,整个聊天服务即不可用。

这正是细节图要解决的问题。


三、分布式扩展架构:从单点走向集群

细节图给出了完整的规模化方案,图中组件包括:

  • 用户层:User A、User B、User C(图中以 3 个用户示意海量用户);
  • 第一级负载均衡:Load Balancer - User to Chat Server Mapping,负责把用户映射到具体的聊天服务器;
  • 聊天服务层:Chat Servers集群,图中含 4 台服务器实例;
  • 存储层:DB Shard数据库分片,图中含 4 个分片实例,每台聊天服务器直连分片读写;
  • 缓存层:Load Balancer - User to Cache Server Mapping负载均衡 +Cache集群(图中 3 台实例),同时DB Shard整体向缓存层供数,即缓存以数据库为数据源。

3.1 分层数据流串联

把图中的箭头连起来,可以得到完整的请求链路:

  1. 用户(User A/B/C)首先命中Load Balancer - User to Chat Server Mapping;
  2. 负载均衡按映射策略把用户分发到Chat Servers集群中的某一台实例;
  3. 聊天服务器直接读写对应的DB Shard(消息按分片规则落到不同数据库实例);
  4. 聊天服务器与数据库都接入缓存层:聊天服务器通过Load Balancer - User to Cache Server Mapping访问Cache集群,数据库分片向缓存层同步/供数,从而把高频读请求从数据库卸载到缓存。

3.2 两个关键设计点

(1)为什么用户到聊天服务器的映射需要专门一个负载均衡?

聊天连接本质上是有状态的:一个用户与某台聊天服务器之间通常维持着 WebSocket 或长轮询连接,服务器侧保存着该用户的会话状态。若负载均衡随机分发,用户每次重连都可能落到不同服务器,导致状态丢失、连接反复重建。因此图中用「User to Chat Server Mapping」明确表达:负载均衡需要按用户维度做映射(可理解为会话保持 / IP hash 之类的粘性策略),让同一用户尽量稳定落在同一台聊天服务器上。

(2)为什么存储要分片、读要缓存?

消息数据具有"按用户/会话天然可分"的特征,适合用DB Shard水平拆分来扩展写入与存储容量;而历史消息、会话列表、用户资料等读取频繁且局部性强,适合引入Cache集群加速,并通过缓存负载均衡把聊天服务器对缓存的访问与数据库解耦。


四、图中关键组件的原理深挖(仓库基础文档佐证)

细节图中的每个组件,在仓库 basics 目录下都有对应的原理文档,下面逐一对应展开。

4.1 负载均衡:聊天的两级 LB

basics/load-balancing.md 指出负载均衡帮助系统"跨不断增多的服务器水平扩展",并给出了三个典型部署位置:

  • 用户与 Web 服务器之间;
  • Web 服务器与内部平台层(应用服务器、缓存服务器)之间;
  • 内部平台层与数据库之间。

细节图正好呈现了前两类:Load Balancer - User to Chat Server Mapping位于用户与聊天服务器之间,Load Balancer - User to Cache Server Mapping位于聊天服务器与缓存之间(此外聊天服务器与 DB 分片之间同样可以再设一层 LB,形成文档所述的第三个位置)。

负载均衡算法方面,仓库列出:最少连接(least connection)、最少响应时间(least response time)、最少带宽(least bandwidth)、轮询(round robin)、加权轮询(weighted round robin)、IP hash。对聊天系统而言,IP hash天然具备"同一来源 IP 落到同一服务器"的特性,可作为用户-聊天服务器映射的简单实现;实现方式上可以是智能客户端、硬件负载均衡器或软件负载均衡器。

4.2 数据库分片:DB Shard 的拆分方式

basics/sharding.md 系统介绍了数据分区方法:

  • 水平分区(range-based sharding):按行拆分到不同表/库,缺点是若分区键选取不当会导致服务器负载不均;
  • 垂直分区:按功能域拆分到独立服务器,实现直观但对应用扩展有后续限制;
  • 目录式分区:通过查找服务抽象分区方案,便于扩库但自身可能成为单点。

分区准则上,聊天消息最常用的是key/hash-based partitioning:对某键属性(如user_id或会话 ID)施加哈希得到分区号;其痛点是新增服务器可能改变哈希函数、需要数据重分布,仓库给出的缓解方案是 consistent hashing——把键与服务器都哈希到环上、顺时针找首个节点,扩容缩容时只需重映射k/n个键,并可引入虚拟副本缓解热点。

分片后还需面对三个常见问题(同样见 basics/sharding.md):跨分片的 join 效率低(可反规范化,但引入数据不一致)、外键等引用完整性难保证(交由应用层维护)、以及数据分布不均时的再平衡。聊天场景通常以用户/会话为分片维度,尽量保证单会话数据落在同一分片内,从而规避跨分片 join。

4.3 缓存层:读多场景的加速器

basics/caching.md 开宗明义:缓存利用局部性原理(最近被请求的数据很可能再次被请求),存在于架构的各层,但通常放在最靠近前端的位置。图中Cache集群被聊天服务器与数据库分片共同引用,属于全局缓存形态——所有请求节点共享一个更快的存储,聊天服务器访问缓存时由缓存服务器处理未命中。

与缓存配套的三个决策点:

  • 缓存失效策略:写直达(write-through,缓存与存储同时写,一致性好但写延迟高)、写旁路(write-around,只写存储,最近写入的数据读时易 miss)、写回(write-back,只写缓存、异步落库,写吞吐高但有宕机丢数风险)——聊天历史消息这类读多写少的场景,写旁路/写回与"数据库向缓存供数"的图示语义更契合;
  • 淘汰策略:FIFO、LIFO、LRU(最近最少使用)、MRU、LFU、随机替换,仓库文档尤其强调 LRU 适用于"丢弃最久未使用"的语义;
  • 分布式缓存 vs 全局缓存:若聊天服务器实例很多,也可用一致哈希把缓存数据分散到各节点(分布式缓存,便于扩容但节点丢失即丢缓存),或像图中这样独立成缓存集群(全局缓存,由缓存服务器统一处理 miss)。

4.4 实时通信:消息推送的协议选型

basics/client-server-communication.md 完整对比了五种通信方式,是"如何让消息实时到达对端"的答案:

  • 标准 HTTP 请求:一问一答,无法推送;
  • Ajax 轮询:客户端周期性(如每 0.5 秒)请求,缺点是需要不停询问、大量空响应造成 HTTP 开销;
  • HTTP 长轮询:服务器延迟响应直到有更新或超时,客户端收到响应后立即发起新请求;每个长轮询请求都有超时,需周期性重连;
  • WebSocket:单条 TCP 连接上的持久全双工通道,握手后双方随时可发数据,通信开销低、实时性高;
  • SSE:服务器向客户端单向推送的实时流,适合服务器循环产生多个事件下行的场景。

对照概述图中 User 与 Chat Server 之间"发送 + 接收"对称的箭头,WebSocket 是最贴合的选型;在兼容性受限时可用 HTTP 长轮询兜底。需要注意的是,长连接会长期占用聊天服务器的连接资源,这也是必须把Chat Servers扩展为集群并做用户-服务器映射的根本原因之一。

4.5 队列与异步化:聊天的可选扩展手段

图中虽未显式画出消息队列,但 basics/queues.md 指出:大规模分布式系统中不同组件需要异步协作,队列是"客户端请求与实际服务工作"之间的抽象——客户端提交任务后无需等待结果,队列还能在服务故障时提供缓冲保护。在 Messenger 场景中,以下工作天然适合异步化:

  • 消息的推送通知(Push Notification)投递;
  • 离线消息的批量落库与上线后的补发;
  • 消息送达回执与已读状态等非关键路径更新。

也就是说,可以在 Chat Server 与存储/推送服务之间插入队列,把实时转发与后台处理解耦,这也是从两张图推演后续演进时可以补充的一环(文档本身未画,故此处仅作设计思路说明)。

4.6 冗余与高可用

basics/redundancy.md 定义冗余为"对关键数据或服务的复制,以提升系统可靠性",典型手段包括服务器故障转移(failover)与共享无状态(shared-nothing)架构——每个节点独立运行、无中央状态管理、新节点可无特殊条件加入、无单点故障。

细节图中的Chat Servers多实例本身就是服务层冗余:单台实例宕机后,负载均衡可以把映射切换到存活实例(配合会话状态外置到缓存/存储,可实现无缝故障转移)。这正是概述图单服务器形态缺失、而细节图补上的高可用能力。

4.7 一致性权衡:CAP 视角

basics/cap-theorem.md 指出:网络分区发生时,必须在一致性与可用性之间二选一——ACID 数据库选择一致性优先,BASE 系统选择可用性优先;而无分区时两者并不冲突。

聊天系统对一致性有不同的层级要求:

  • 消息内容与顺序:同一会话内的消息必须有序、不丢失,通常要求强一致或至少会话级有序;
  • 已读状态、在线状态:可以接受短暂不一致(最终一致),换取高可用与低延迟。

因此在存储层选型与复制策略上,需要按数据类别区分对待,这正是 basics/cap-theorem.md 在 Messenger 设计中的具体落点。

4.8 存储选型:SQL vs NoSQL

basics/sql-vs-nosql.md 给出了选型框架:SQL 以固定 schema、ACID 事务、纵向扩展见长;NoSQL 以动态 schema、横向扩展(加机器即可扩容)、牺牲部分 ACID 换取性能见长,常见形态包括键值存储(Redis、Dynamo)、文档库(MongoDB、CouchDB)、宽列库(Cassandra、HBase)与图数据库(Neo4J)。

对聊天消息而言:

  • 消息记录天然按会话/用户聚合,文档或宽列模型的"按 key 聚合读取"特性贴合"拉取某会话最近 N 条消息"的查询模式;
  • 海量消息需要水平扩展,NoSQL 的廉价横向扩展更契合,且可与 basics/sharding.md 的分片方案叠加;
  • 若需要强事务(如转账类业务消息)则 SQL 更合适。

仓库其他设计文档也体现了类似判断——例如 designs/short-url.md 在存储观察中明确写出"NoSQL 更易扩展,SQL 加分片也可行",可作为同仓库内选型思路的参照。


五、回到方法论:如何用这两张图讲透一个系统设计

README.md 将系统设计面试流程归纳为三步:

  1. 范围界定(Scope the problem):不要假设,通过澄清问题理解约束与用例,包括需求澄清与系统接口定义;
  2. 抽象设计(Sketch up an abstract design):列出系统的积木与关系,包括粗略估算(back-of-the-envelope estimation)、定义数据模型、高层设计;
  3. 识别并解决瓶颈(Identify and address the bottlenecks):进入详细设计,找出瓶颈并解决。

facebook-messenger.md的两张图恰好是这套方法的可视化缩影:概述图 = 第 2 步的高层设计(先画出最小可运行系统),细节图 = 第 3 步的详细设计与瓶颈解决(单点 → 集群、单库 → 分片、无缓存 → 缓存层)。

如果想在面试中把这两张图扩展成一份完整的设计稿,可以参照仓库中结构最完整的 designs/short-url.md,它通常覆盖这些章节:功能/非功能/扩展需求、容量估算(QPS、存储、带宽、缓存内存)、系统 API(含参数表)、数据库 schema、核心算法、数据分区与复制、缓存策略(LRU、更新时机)、负载均衡位置、过期数据清扫、遥测统计与安全权限。对 Messenger 而言,对应的推演方向包括:

  • 容量估算:假设读多写少(如 100:1 读:写比),估算每秒消息量与存储增长(需自行设定合理假设,文档未给具体数字,不可臆造);
  • 数据模型:消息表(消息 ID、发送者、接收者/会话 ID、内容、时间戳)、用户表、会话表;
  • 分区:按用户/会话哈希分片(见 4.2);
  • 缓存:缓存最近会话与热用户状态,LRU 淘汰(见 4.3);
  • 实时通道:WebSocket 为主、长轮询兜底(见 4.4)。

六、小结

facebook-messenger.md用两张图完成了"从 0 到 1、从 1 到 N"的完整叙事:概述图中的User ↔ Chat Server ↔ Data storage是聊天系统不可再简化的核心骨架,细节图则在每个单点上做文章——用「用户-聊天服务器映射负载均衡」解决有状态连接的归属问题,用Chat Servers集群解决连接与转发容量问题,用DB Shard解决存储与写入扩展问题,用Cache集群解决读放大问题。四个方向分别对应仓库基础文档中的负载均衡、冗余、分片(含一致哈希)与缓存原理,而实时通信选型(WebSocket/长轮询)、异步队列与 CAP 权衡则是聊天系统区别于普通 Web 系统的独特维度。掌握这张图背后的组件职责与取舍逻辑,即可在面试中把任何聊天类系统设计题讲得有条理、有依据。

延伸阅读:仓库 basics 目录下的 load-balancing.md、sharding.md、caching.md、client-server-communication.md、queues.md、redundancy.md、cap-theorem.md、sql-vs-nosql.md、consistent-hashing.md,以及 README.md 中的系统设计面试方法论,都是深入这一主题的配套材料。

  • 教程

【免费下载链接】Grokking-System-Design

Systems design is the process of defining the architecture, modules, interfaces, and data for a system to satisfy specified requirements. Systems design could be seen as the application of systems theory to product development.

项目地址:https://gitcode.com/gh_mirrors/gr/Grokking-System-Design
点击查看免费下载
上一篇:Apache DolphinScheduler Doris 数据源接入指南:参数详解、驱动安装与 MySQL 兼容连接原理
下一篇:@scalar/fastify-api-reference 插件全解析:为 Fastify 应用接入 Scalar API Reference 的完整实践指南

创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询