系统设计实例(四)日活5000万的聊天系统
设计一个日活5000万的聊天系统
- 聊天服务器促进消息的发送/接收。
- 在线状态服务器管理在线/离线状态。
- API服务器处理所有事情,包括用户登录、注册、更改个人资料等。
- 通知服务器发送推送通知。
- 最后,键值存储被用来存储聊天历史。当一个离线用户上线时,她将看到所有以前的聊天历史。
数据存储
第一种是通用数据,如用户资料、设置、用户好友列表。这些数据存储在稳健和可靠的关系数据库中。复制和分片是常用的技术,用来满足可用性和可扩展性的需求。
第二种是聊天系统独特的数据:聊天历史数据,采用键值存储。
- 键值存储允许轻松的水平扩展。
- 键值存储提供非常低的数据访问延迟。
- 关系数据库不能很好地处理数据的长尾[3]。当索引变大时,随机访问变得昂贵。
数据模型
1对1聊天的消息表,主键是 message_id ,群聊的消息表。复合主键是 (channel_id, message_id),message_id的责任:
- ID必须是唯一的。
- ID应该按时间排序,意味着新行的ID比旧的更高。
## 如何实现消息ID自增?
MySql 中的“ auto_increment ”关键字。然而,NoSQL数据库通常不提供这样的特性。
Snowflake 全局64位序列号生成器
本地序列号生成器。本地意味着ID只在一个组内是唯一的。本地ID起作用的原因是,只需维护一对一频道或群组频道内的消息顺序就足够了。与全局ID实现相比,这种方法更容易实现。
服务发现
根据诸如地理位置、服务器容量等标准,为客户端推荐最好的聊天服务器。Apache Zookeeper是一个流行的开源服务发现解决方案。它注册所有可用的聊天服务器,并根据预定的标准为客户端选择最好的聊天服务器。
1对1消息流程
- 用户A向聊天服务器1发送聊天消息。
- 聊天服务器1从ID生成器获取消息ID。
- 聊天服务器1将消息发送到消息同步队列。
- 消息存储在键值存储中。
5.a. 如果用户B在线,消息将转发到用户B连接的聊天服务器2。
5.b. 如果用户B离线,推送通知(PN)服务器会发送推送通知。 - 聊天服务器2将消息转发给用户B。用户B和聊天服务器2之间存在持久的WebSocket连接。
消息跨设备同步
每个设备都维护了一个名为cur_max_message_id的变量,该变量跟踪设备上最新的消息ID。满足以下两个条件的消息被认为是新消息:
接收方ID等于当前登录的用户ID。
键值存储中的消息ID大于cur_max_message_id
由于每个设备上的cur_max_message_id都不同,每个设备都可以从KV存储中获取新消息,因此消息同步很容易。
群聊消息流程
消息被复制到每个群成员的消息同步队列,可以看作是接收者的收件箱。这种设计选择对小群聊很有好处:
- 它简化了消息同步流程,因为每个客户端只需要检查自己的收件箱就可以获取新消息。
- 当群数量较小时,将副本存储在每个接收者的收件箱中并不太昂贵。(微信500人)
在线状态
在客户端和实时服务之间建立WebSocket连接后,用户A的在线状态和last_active_at时间戳会保存在KV存储中。用户登录后,状态指示器会显示用户在线。
心跳机制:定期地,在线客户端向状态服务器发送一个心跳事件。如果状态服务器在一定时间内,比如从客户端开始的x秒内,接收到一个心跳事件,用户就被认为是在线的。否则,就是离线的。
状态服务器使用发布-订阅模型,其中每对朋友都维持一个通道。当用户A的在线状态发生变化时,它将事件发布到所有的通道,朋友们很容易获得在线状态的更新,对于小用户群体是有效的。大群体的解决方案是只有当用户进入一个群或手动刷新好友列表时才获取在线状态
原文地址:https://blog.csdn.net/qq_42767993/article/details/136985181
免责声明:本站文章内容转载自网络资源,如本站内容侵犯了原著者的合法权益,可联系本站删除。更多内容请关注自学内容网(zxcms.com)!