设计 WhatsApp/微信:实时通讯系统架构
FreeGuideOnline
最新
2026-06-18
设计 WhatsApp/微信:实时通讯系统架构
本教程带你从零开始理解大型实时聊天系统(如 WhatsApp、微信)的底层架构设计。你将掌握核心组件、消息路由、状态同步与高可用策略,无需庞大背景知识。
1. 系统核心需求与挑战
在设计架构前,先明确业务要求和隐藏的技术难点。
功能需求一览
- 一对一聊天:实时发送文本、图片、视频、文件。
- 群组聊天:支持千人级别群组。
- 在线状态展示:在线、离线、最后上线时间。
- 消息已读回执:单勾、双勾、蓝色双勾。
- 消息历史记录:永久保存,多设备同步。
- 推送通知:即使 App 在后台也能收到。
非功能性需求(来自亿级用户的考验)
- 低延迟:消息端到端延迟 < 200ms。
- 高可靠:消息不丢失、不重复(精确一次)。
- 高并发:同时支撑数亿长连接。
- 安全性:端到端加密(E2EE)。
- 水平扩展:随用户量平滑扩容。
2. 架构全景图
大型聊天系统并非简单的客户端-服务器直连,而是一个分层的分布式系统。
用户A客户端 ──> 长连接网关 ──> 消息队列 ──> 消息处理服务 ──> 消息存储
└──> 状态服务 / 群组服务
用户B客户端 <── 长连接网关 <──────────────┘
这个逻辑分层包含以下核心角色:
- 接入网关:管理海量客户端的 WebSocket/TCP 长连接。
- 消息路由:决定消息如何送达目标用户。
- 业务服务:处理群组、好友关系、在线状态。
- 持久化层:消息存储、时序、索引。
- 多媒体处理:图片压缩、视频转码。
- 推送通道:离线时通过 APNs/FCM 强提醒。
3. 连接层:长连接网关设计
传统 HTTP 短轮询无法满足即时性,聊天系统采用 持久连接。
3.1 技术选型:WebSocket vs 自研 TCP
- WebSocket:浏览器级兼容,适用于 Web 与简化开发。微信网页版曾使用。
- 自研私有 TCP:更节省流量(二进制协议),心跳自定义,用于移动 App。WhatsApp 使用 MQTT 改进版或 XMPP+定制。
3.2 网关集群架构
几十亿设备不可能全连一台服务器,采用 连接路由 + 网关集群:
- 客户端通过 DNS 或负载均衡选择合适的边缘节点(就近接入)。
- 网关节点维护 用户ID → 连接实例 映射(存在于一致性哈希分片中)。
- 网关本身无状态,状态存储在 Redis 或专用状态服务中,重启不影响连接(连接转移或重连机制)。
3.3 心跳与离线检测
- 客户端定时发送心跳包(如每 30 秒)。
- 若 3 个心跳周期无响应,网关将用户标为离线,并通知状态服务。
- 离线后消息转为推送通知,未捎带的消息缓存在服务端,上线后拉取。
4. 消息核心流程:从发出到送达
以一对一消息为例,详细拆解时序:
4.1 发送路径
- 客户端A 通过长连接发送消息(
{to: B, content: "hello", msgId: M1})。 - 网关收到后,先返回发送确认(减少客户端重试),然后将消息投递到内部消息队列(Kafka)。
- 消息处理服务消费队列:
- 入库存储(消息持久化)。
- 解析接收方 B。
- 从状态服务查询 B 的在线网关节点。
- 如果 B 在线,消息直接通过网关间内部通道推送给 B 所在网关;网关再下行给客户端,携带服务端生成的全局序列号 seqID。
- 如果 B 离线,消息存入待收队列;B 上线后通过 sync 协议 拉取。
4.2 已读回执多级设计
- 服务器已收到:发送方看到“单勾”。
- 已送达目标设备:目标网关成功下发,确认回执,发送方看到“双勾”。
- 对方已读:用户 B 打开对话时,客户端上报
read事件拉到最大 seqID,服务端更新会话已读游标,并通知 A。
4.3 消息ID与去重
- 客户端生成全局唯一
msgId(UUID 或时间戳+设备ID+递增数)。 - 服务端分配单调递增的
seqID,用于历史拉取有序。 - 通过消息ID去重表防止重复发送(针对网络重试)。
5. 群组消息分发:Fan-out 模型
群组消息是性能瓶颈,对于 5000 人群,不能逐用户写扩散。
5.1 写扩散 vs 读扩散
- 写扩散:发到群时,将消息复制到每个成员的“收件箱”。此方式查询简单,但大群写入爆炸。
- 读扩散:消息只存一份到群消息表,成员阅读时实时拉取。查询压力大,但写入恒定。
实用方案:混合模式(大小群区分)
- 小型群(<100 人):使用写扩散,每个成员拥有独立时间线,拉取快;
- 大型群(>100 人):读扩散,消息只写一份,成员查看时动态拉取,并缓存热点群消息。
5.2 超大群优化
- 限制群成员数上限(如微信 500 人,可付费扩增)。
- 在线用户实时推送;离线用户不推送所有消息,仅记录未读计数,上线后增量拉取。
- 群消息降级:非活跃成员降低实时推送优先级。
6. 消息同步与多设备方案
用户可能同时登录手机、PC、Web,要求消息在多端实时同步。
6.1 全局消息序列号
每个用户维护一条同步时间线,所有设备共享相同的递增 seqID(用户级)。
- 服务端为每用户分配一个原子递增序列。
- 会话内的消息记录通过
(user_id, seq_id)索引。 - 新设备登录,上报本地最大 seq,服务端返回增量数据。
6.2 会话列表与状态同步
- 会话信息(最后一条消息、未读计数等)通过轻量级同步协议更新,减少全量推送。
- 多设备读回执:设备 A 读某消息后,向服务端上报已读 seq,服务端将游标同步给设备 B,B 自动消除未读。
7. 存储设计:消息、会话与关系链
7.1 消息存储结构
- 用户消息表(按用户分片):
user_id+seq_id做主键,存储内容、发送方、时间、状态。 - 群组消息表(按群分片):
group_id+tg_seq_id主键。 - 消息内容支持 JSON 扩展,不同类型消息(文本、图像、视频)用
content_type区分,媒体文件存独立对象存储(如 S3/CDN),消息体中只保留文件 URL。
7.2 冷热数据分离
- 近 30 天热消息存高性能 SSD 数据库(如 MySQL 或 HBase);
- 历史消息归档至低成本的分布式存储(Cassandra、HDFS),用户检索时查询归档层。
7.3 索引与查询
- 根据
to_user_id拉取会话消息,需要联合索引(to_user_id, seq_id)。 - 图片/文件搜索依靠媒体服务元数据库,与消息内容解耦。
8. 在线状态系统设计
用户“在线/离线/最后上线”需要近实时传播。
8.1 状态传播模型
- 状态变更由 状态服务 管理,连接层上报。
- 采用发布订阅模式:A 好友上线,订阅了 A 的状态的好友都会收到变更通知。
- 为节省广播带宽,推送间隔做限流(例如每 60 秒只推送一次上线/下线批量更新)。
8.2 最终一致性
- 实时穿透可能延迟,采用弱一致性,用户列表显示的状态可以有几秒滞后。
- “正在输入”状态通过聊天信道临时高频推送,不属于持久状态。
9. 多媒体消息与附件处理
图片、视频等大文件不能通过长连接信道传输。
9.1 发送流程
- 客户端上传文件到媒体服务器,获得临时 ID。
- 媒体服务器对图片进行压缩、生成缩略图;视频进行转码(不同分辨率)。
- 上传完成后返回最终 CDN URL 和文件信息哈希。
- 客户端将元信息(尺寸、格式、URL)打包进聊天消息发送。
9.2 接收端展示
- 接收方收到消息后,根据 URL 从 CDN 加载缩略图直接显示,高清原图按需下载。
- 端到端加密时,文件需在客户端加密后上传,服务端保存加密后的文件,接收方下载后解密。
10. 高可用与扩展性设计
10.1 服务质量保证
- 避免单点故障:每个服务多活部署,连接网关宕机,客户端自动重连转移到其他节点。
- 消息可靠性:消息队列持久化 + 至少一次语义,业务层做幂等处理。
- 异地多活:核心服务分布在多个数据中心,用户就近接入,数据通过专线同步。
10.2 水平扩展关键点
- 网关层:依据用户 ID 一致性哈希分片,新增加节点自动重新平衡连接。
- 消息处理:Kafka 分区按
to_user_id划分,保证同一用户消息顺序处理。 - 存储层:用户分库分表,使用用户 ID 后几位取模;群组消息按群分表。
- 缓存:热点用户会话列表、群信息缓存在 Redis 集群,拦截极大量读请求。
11. 安全与加密(端到端加密概览)
对于注重隐私的聊天系统,需实现 E2EE:
- 密钥协商:基于 Signal 协议(双棘轮),通过 X3DH 和定期更新会话密钥。
- 服务端透明:消息在发送端加密,服务端只负责存储密文和路由,无法解密。
- 群聊加密:采用“发件人密钥”模型(Sender Key)降低加密开销。
- 身份验证:带外比对安全码(Safety Number)防止中间人攻击。
注:E2EE 实现复杂,但对架构影响主要在消息处理流程中,消息内容无法被服务端解析,搜索功能需在客户端索引或使用同态加密等高级技术(目前很少实现)。
12. 总结:架构演进路线
首先实现 MVP:
- 单机 WebSocket 服务器
- 内存用户连接映射
- 单库存储消息
逐步演进为分布式: 4. 增加网关集群,连接分片 5. 引入消息队列解耦收发 6. 用户消息表分库分表 7. 群组写扩散→混合扩散 8. 多媒体独立通道 9. 异地多活与 E2EE 集成
掌握这些原则,你就能按照实际规模裁剪系统,设计出可靠且可扩展的聊天平台。
本教程由「免费在线教程」提供,期待你动手实践架构绘图与模拟实现。