1. 系统的要求和目标
1.1 功能要求
对话:系统应支持用户之间的一对一和群组对话。
确认消息:系统应支持消息传递确认,如已发送、已送达、已读。
共享:系统应支持媒体文件的共享,例如图像、视频和音频。
聊天存储:系统必须支持用户离线时聊天消息的持久存储,直到消息成功传递。
推送通知:一旦离线用户的状态变为在线,系统应该能够向其通知新消息。
1.2 非功能性需求
低延迟:用户应该能够以低延迟接收消息。
一致性:消息应该按照发送的顺序传递。此外,用户必须在所有设备上看到相同的聊天历史记录。
可用性:系统应该具有高可用性。然而,一致性比可用性更重要。
安全性:系统必须通过端到端加密来保证安全。我们需要确保只有通信双方才能看到消息的内容。中间的任何人,甚至我们作为服务所有者,都不应有权访问。
可扩展性:系统应该具有高度可扩展性,以支持每天不断增加的用户和消息数量。
2. 高层系统设计
2.1 通讯方式
首先,我们需要了解客户端 和服务器如何通信。在聊天系统中,客户端可以是移动应用程序或 Web 应用程序。客户端之间不直接通信。每个客户端都连接到一个聊天服务,该服务支持我们之前讨论的所有功能:
接收来自其他客户端的消息。
为每条消息找到正确的收件人,并将消息转发(传递)给收件人。
如果收件人不在线,则需要在服务器上保留该收件人的消息,直到他们在线为止。
由于 HTTP 是客户端发起的,我们无法真正从服务器 向接收者发送消息,因此我们需要考虑用于模拟服务器发起的连接的其他技术:轮询、长轮询和 WebSocket。
轮询:是客户端定期向服务器请求数据,产生大量请求,效率低下。
长轮询:服务器保持连接打开,直到有新数据可用,从而减少请求数量和延迟。
WebSocket:是一种双向通信协议,可通过单个长期连接实现客户端和服务器之间的实时通信,从而提供最低的延迟。它是从服务器向客户端发送异步更新的最常见解决方案。
两个客户端之间的通信步骤如下:
用户A和用户B创建与聊天服务器的通信通道。
用户A向聊天服务器发送消息。
当收到消息时,聊天服务器会向用户 A 回复确认消息。
如果接收者的状态为离线,则聊天服务器将消息发送给用户B,并将消息存储在数据库中。
用户 B 向聊天服务器发送确认消息。
聊天服务器通知用户A消息已成功发送。
当用户 B 阅读消息时,应用程序通知聊天服务器。
聊天服务器通知用户A用户B已阅读消息。
对于客户端-服务器聊天通信,WebSocket 优于 HTTP(S) 协议,因为 HTTP(S) 不会保持连接打开以供服务器频繁向客户端发送数据。使用 HTTP(S) 协议时,客户端不断向服务器请求更新,这会占用大量资源并导致延迟。WebSocket 在客户端和服务器之间维护持久连接。只要数据可用,该协议就会立即将数据传输到客户端。它提供了双向连接,用作将异步更新从服务器发送到客户端的通用解决方案。 其他一切非聊天内容不一定都是通过 WebSocket协议。事实上,聊天应用程序 的大多数功能(注册、登录、用户配置文件等)都可以使用基于 HTTP 的传统请求/响应方法。
2.2 高级组件
聊天系统分为三大类:无状态服务、有状态服务、第三方集成。
无状态服务:是传统的基于HTTP的请求/响应服务,用于管理登录、注册、用户配置文件等。它们位于负载均衡服务后面,负载均衡服务的工作是根据请求路径将请求路由到正确的服务。这些服务可以是整体的或单独的微服务。我们不需要自己构建许多无状态服务,因为市场上有可以轻松集成的服务。我们将深入讨论的一项服务是服务发现。它的主要工作是向客户端提供客户端可以连接的聊天服务器的 DNS 主机名列表。
有状态服务:唯一有状态的服务是聊天服务。该服务是有状态的,因为每个客户端都维护与聊天服务器的持久网络连接。在此服务中,只要服务器仍然可用,客户端通常不会切换到另一个聊天服务器。服务发现与聊天服务密切配合,以避免服务器过载。
第三方集成:用于推送通知,以便在新消息到达时通知用户,即使应用程序未运行也是如此。
2.3 存储
我们需要考虑使用哪种类型的数据库:关系数据库还是NoSQL。在典型的聊天系统中,我们将有两种类型的数据。 第一个是通用数据,例如用户个人资料、设置和用户好友列表。这些数据应该存储在可靠的关系数据库中。我们需要实现复制和分片来满足可用性和可扩展性要求。 第二个是聊天系统特有的:聊天历史数据。