第12章:设计聊天系统
简介
A聊天系统支持用户之间的实时消息传递。本章重点探讨如何设计一款包含以下功能的聊天应用:
•
一对一聊天、群聊(最多100人)、在线状态显示、多设备支持、推送通知
步骤1:理解问题
Requirements
功能:
◦
一对一与群聊(最多100名成员)、基于文本的消息(最长可达10万字符)。
◦
在线/离线状态显示、支持多设备、推送通知。
扩展性:面向5000万日活跃用户设计。
存储:永久聊天记录。
步骤2:概要设计
Communication Protocols
发送端:使用HTTP协议发送消息,并通过持久连接提高效率。

接收端:
◦
轮询:
▪
客户端定期向服务器查询是否有可用消息。
▪
由于频繁且冗余的请求而效率低下。

Components
无状态服务:
◦
处理用户注册、登录及个人资料管理。
◦
与服务发现集成,以推荐最佳聊天服务器。
有状态服务:
◦
聊天服务器维持持久的WebSocket连接。
◦
负责消息的投递与同步。
第三方集成:
◦
推送通知服务用于向用户通知新消息。
◦
有关通知的实现,请参阅“通知系统”章节。
Design
客户端通过持久化的WebSocket连接与聊天服务器保持通信,以实现实时消息传递。
•
聊天服务器负责消息的发送与接收。
•
在线状态服务器管理用户的在线/离线状态。
•
API服务器处理所有业务,包括用户登录、注册、修改个人资料等。
•
通知服务器负责推送通知的发送。
•
最后,键值存储用于保存聊天记录。之所以采用键值存储来存放聊天历史数据,主要有以下几个原因:
◦
它支持轻松的水平扩展。
◦
KV存储能够以极低的延迟访问数据。
◦
关系型数据库难以应对数据的长尾问题。当索引规模增大时,随机访问的开销会显著增加。
◦
KV存储被其他经过验证的可靠聊天应用所采用。例如,Facebook Messenger和Discord都使用了它。
以下是单聊和群聊的数据模型。
•
主键是消息ID,有助于确定消息的顺序。
•
对于群聊,复合主键为(channel_id, message_id)。
◦
可以使用像Snowflake这样的全局64位序列号生成器来生成ID。
◦
更好的做法是使用本地序列号生成器。所谓本地,是指ID仅在单个分组内唯一。
◦
本地ID之所以有效,是因为只需在一对一聊天或群聊中维护消息的顺序即可。
50%
50%
步骤3:设计深度剖析
Service Discovery
•
服务发现的主要作用是根据地理位置、服务器容量等标准,为客户端推荐最合适的聊天服务器。
•
用途Apache Zookeeper用于根据地理位置和服务器容量等标准分配聊天服务器。
•
确保负载均衡高效,并最大限度地降低延迟。
Messaging Flows
一对一聊天
用户A向聊天服务器1发送一条消息。
聊天服务器1分配一个唯一的消息ID,并将该消息存储在键值存储中。
如果用户B在线,该消息将被转发至聊天服务器2,并保持持久化的WebSocket连接。
如果用户B处于离线状态,则发送一条推送通知。
群聊
•
消息会被复制到群组中每位收件人的个人收件箱。
•
这简化了同步过程,但对大型群组而言成本较高。
•
在接收端,一个接收者可以收到来自多个用户的消息。每个接收者都拥有一个收件箱(消息同步队列),其中存放着来自不同发件人的消息。
消息同步
许多用户拥有多个设备。我们需要在各设备之间同步消息。每个设备都维护一个名为 cur_max_message_id 的变量,用于记录该设备上最新的消息 ID。满足以下两个条件的消息被视为新闻消息:
•
收件人ID等于当前登录用户的ID。
•
键值存储中的消息ID大于当前最大消息ID
Online Presence
心跳机制:
扇出模型:
其他注意事项
Scalability
•
水平扩展:随着用户数量增加,动态添加服务器。
•
负载均衡:将流量均匀分配到各台服务器上。
•
缓存:降低数据库负载,提升响应速度。
Error Handling
•
重试机制:通过重试和队列处理消息投递失败。
•
服务器故障:在发生故障时,利用服务发现机制分配新服务器。
Future Extensions
媒体支持:增加对照片和视频的处理功能,包括压缩与云存储。
端到端加密:确保消息隐私。
客户端缓存:减少数据传输,提升性能。
提升加载速度:采用地理分布式的缓存网络。