共计 7843 个字符,预计需要花费 20 分钟才能阅读完成。
本文章参考 https://systeminterview.com/d…,有删减。
在本章中,咱们将探讨聊天零碎的设计。简直每个人都应用聊天应用程序。图 12- 1 显示了市场上一些最风行的应用程序。
聊天利用为不同的人执行不同的性能。确定确切的要求是极其重要的。例如,当面试官思考一对一聊天时,您不心愿设计一个专一于个体聊天的零碎。摸索性能需要是很重要的。
步骤 1 - 确定需要
当然,世界上没有最完满的架构,只有最合适的架构,也没有所谓的通用计划,不同的解决方案都有其优缺点,只有最满足业务的零碎才是一个好的零碎。而且,在无限的人力、物力,综合思考工夫老本,通常须要做出很多衡量。
就要设计的聊天应用程序的类型达成统一至关重要。在市场上,有 Facebook Messenger、微信和 WhatsApp 等一对一聊天利用,Slack 等专一于群聊的 office 聊天利用,Discord 等专一于大群体互动和低语音聊天提早的游戏聊天利用。
第一组廓清问题应该明确面试官在要求你设计聊天零碎时的具体想法。至多,弄清楚你是应该专一于一对一聊天还是群聊利用。您可能会问以下问题:
候选人:咱们应该设计什么样的聊天应用程序?1 对 1 还是基于组?面试官:它应该反对 1 对 1 和群聊。
候选人:这是一个挪动应用程序吗?还是网络应用?或者两者都有?面试官:都有。
候选人:这个应用程序的规模是多少?守业利用还是大规模?面试官:它应该反对 5000 万每日沉闷用户(DAU)。
候选人:对于群组聊天,群组成员限度是多少?面试官:最多 100 人
候选人:聊天应用程序的重要性能是什么?它能反对附件吗?面试官:1 对 1 聊天,群聊,在线指示器。零碎仅反对文本音讯。
候选人:邮件大小有限度吗?面试官:是的,文本长度应该少于 100000 个字符。
候选人:须要端到端加密吗?面试官:当初不须要,但如果工夫容许,咱们会探讨的。
候选人:咱们应该将聊天记录存储多久?面试官:永远。
在本章中,咱们将重点设计一款相似 Facebook messenger 的聊天应用程序,重点介绍以下性能:
•一对一聊天,传递提早低
•小组聊天(最多 100 人)
•在线状态
•多设施反对。同一帐户能够同时登录到多个帐户。
•推送告诉
就设计规模达成统一也很重要。咱们将设计一个反对 5000 万 DAU 的零碎。
第 2 步 - 设计
为了开发高质量的设计,咱们应该具备客户机和服务器如何通信的基本知识。在聊天零碎中,客户端能够是挪动应用程序或 web 应用程序。客户机之间不间接通信。相同,每个客户端都连贯到一个聊天服务,该服务反对上述所有性能。让咱们关注根本业务。聊天室服务必须反对以下性能:
•接管来自其余客户端的音讯。
•为每条音讯找到适合的收件人,并将音讯转发给收件人。
•如果收件人不在线,则在服务器上保留该收件人的音讯,直到其在线。
图 12- 2 显示了客户端(发送方和接管方)与聊天服务之间的关系。
当客户端打算启动聊天时,它会应用一个或多个网络协议连贯聊天服务。对于聊天服务,网络协议的抉择很重要。让咱们和面试官讨论一下。
对于大多数客户机 / 服务器应用程序,申请由客户机发动。对于聊天应用程序的发送方来说也是如此。在图 12- 2 中,当发送方通过聊天服务向接管方发送音讯时,它应用通过工夫测试的 HTTP 协定,这是最常见的 web 协定。在此场景中,客户端关上与聊天服务的 HTTP 连贯并发送音讯,告诉服务将音讯发送给接管方。keep-alive 在这方面很无效,因为 keep-alive 头容许客户端与聊天服务放弃长久连贯。它还缩小了 TCP 握手的次数。HTTP 在发送方是一个很好的抉择,许多风行的聊天应用程序(如 Facebook[1])最后应用 HTTP 发送音讯。
然而,接收器端要简单一些。因为 HTTP 是由客户机发动的,因而从服务器发送音讯并不简略。多年来,许多技术被用来模仿服务器启动的连贯:轮询、长轮询和 WebSocket。这些都是零碎设计面试中宽泛应用的重要技巧,所以让咱们来钻研一下它们。
轮询
如图 12- 3 所示,轮询是一种客户端定期询问服务器是否有可用音讯的技术。依据轮询频率,轮询的老本可能会很高。它可能会耗费贵重的服务器资源来答复一个在大多数状况下都没有答案的问题。
长轮询
因为轮询可能效率低下,下一步是长轮询(图 12-4)。
在长轮询中,客户端放弃连贯关上,直到有新音讯可用或达到超时阈值。一旦客户端接管到新音讯,它会立刻向服务器发送另一个申请,从而重新启动过程。长轮询有几个毛病:
•发送方和接管方可能无奈连贯到同一聊天服务器。基于 HTTP 的服务器通常是无状态的。如果应用循环法进行负载平衡,则接管音讯的服务器可能与接管音讯的客户端没有长轮询连贯。
•服务器无奈很好地判断客户端是否已断开连接。
•效率低下。如果用户聊天不多,长轮询依然会在超时后进行定期连贯。
websocket 长连贯
WebSocket 是从服务器向客户端发送异步更新的最常见解决方案。图 12- 5 显示了其工作原理。
WebSocket 连贯由客户端启动。它是双向和长久的。它从 HTTP 连贯开始,能够通过一些定义良好的握手“降级”到 WebSocket 连贯。通过这种长久连贯,服务器能够向客户端发送更新。即便有防火墙,WebSocket 连贯通常也能失常工作。这是因为它们应用的端口 80 或 443 也被 HTTP/HTTPS 连贯应用。
后面咱们说过,在发送方应用 HTTP 是一种很好的协定,然而因为 WebSocket 是双向的,因而没有强有力的技术理由不将其用于发送。图 12- 6 显示了如何将 WebSocket(ws)用于发送方和接管方。
通过将 WebSocket 用于发送和接管,它简化了设计,并使客户端和服务器上的实现更加简略。因为 WebSocket 连贯是长久的,因而高效的连贯治理在服务器端至关重要。
高级设计
方才咱们提到 WebSocket 被选为客户端和服务器之间双向通信的次要通信协议,须要留神的是,其余所有都不用是 WebSocket。事实上,聊天应用程序的大多数性能(注册、登录、用户配置文件等)都能够通过 HTTP 应用传统的申请 / 响应办法。让咱们深刻理解一下零碎的高级组件。
如图 12- 7 所示,聊天零碎分为三大类:无状态服务、有状态服务和第三方集成。
无状态服务
无状态服务是传统的面向公众的申请 / 响应服务,用于治理登录、注册、用户配置文件等。这些是许多网站和应用程序的常见性能。
无状态服务位于负载平衡器前面,负载平衡器的工作是依据申请门路将申请路由到正确的服务。这些服务能够是繁多的或单个的微服务。咱们不须要本人构建这些无状态服务中的许多,因为市场上存在能够轻松集成的服务。咱们将深刻探讨的一项服务是服务发现。它的次要工作是为客户端提供一个能够连贯到的聊天服务器的 DNS 主机名列表。
有状态服务
惟一有状态的服务是聊天服务。该服务是有状态的,因为每个客户端都放弃与聊天服务器的长久网络连接。在该服务中,只有服务器依然可用,客户端通常不会切换到另一个聊天服务器。服务发现与聊天服务密切配合,以防止服务器过载。咱们将深入探讨细节。
第三方集成
对于聊天应用程序,推送告诉是最重要的第三方集成。这是一种在新音讯达到时告诉用户的办法,即便应用程序未运行。推送告诉的正确集成至关重要。无关更多信息,请参阅第 10 章设计告诉零碎。
可伸缩性
在小范畴内,下面列出的所有服务都能够装置在一台服务器中。即便依照咱们设计的规模,实践上也能够在一台古代云服务器中包容所有用户连贯。服务器能够解决的并发连接数很可能是限度因素。在咱们的场景中,在一百万并发用户的状况下,假如每个用户连贯在服务器上须要 10K 的内存(这是一个十分粗略的数字,并且十分依赖于语言选择),它只须要大概 10GB 的内存就能够在一个框中包容所有连贯。
如果咱们提出一个设计,所有的货色都放在一台服务器上,这可能会在面试官的脑海中升起一个微小的危险信号。没有一个技术专家会在一台服务器上设计这样的规模。因为许多因素,单服务器设计是交易的破坏者。单点故障是其中最大的故障。
然而,从繁多服务器设计开始是完全正确的。确保面试官晓得这是一个终点。将咱们提到的所有内容放在一起,图 12- 8 显示了调整后的高级设计。
在图 12- 8 中,客户端放弃与聊天服务器的长久 WebSocket 连贯,以进行实时消息传递。
•聊天服务器便于发送 / 接管音讯。
•状态服务器治理在线 / 离线状态。
•API 服务器解决所有,包含用户登录、注册、更改配置文件等。
•告诉服务器发送推送告诉。
•最初,键值存储用于存储聊天历史记录。当离线用户联机时,她将看到以前的所有聊天记录。
存储
当初,咱们曾经筹备好了服务器,服务正在运行,第三方集成曾经实现。在技术层的深处是数据层。数据层通常须要一些致力能力使其正确。咱们必须做出的一个重要决定是抉择正确的数据库类型:关系数据库还是 NoSQL 数据库?为了做出理智的决定,咱们将查看数据类型和读 / 写模式。
典型的聊天零碎中存在两种类型的数据。第一种是通用数据,例如用户配置文件、设置、用户好友列表。这些数据存储在强壮牢靠的关系数据库中。复制和分片是满足可用性和可伸缩性要求的罕用技术。
第二个是聊天零碎特有的:聊天历史数据。了解读 / 写模式很重要。
•聊天零碎的数据量微小。此前的一项钻研 [2] 显示,Facebook messenger 和 Whatsapp 每天解决 600 亿条音讯。
•只常常拜访最近的聊天。用户通常不会查找旧聊天记录。
•只管在大多数状况下都会查看最近的聊天记录,但用户可能会应用须要随机拜访数据的性能,如搜寻、查看您的提及、跳转到特定音讯等。数据拜访层应反对这些状况。
•1 对 1 聊天利用的读写比约为 1:1。
抉择反对咱们所有用例的正确存储系统至关重要。出于以下起因,咱们举荐键值存储:
•键值存储容许轻松程度缩放。
•键值存储提供非常低的数据拜访提早。
•关系数据库不能很好地解决长尾 [3] 数据。当索引变大时,随机拜访的代价很高。
•键值存储被其余教训证的牢靠聊天应用程序采纳。例如,Facebook messenger 和 Discord 都应用键值存储。Facebook messenger 应用 HBase[4],Discord 应用 Cassandra[5]。
数据模型
方才,咱们探讨了应用键值存储作为存储层。最重要的数据是音讯数据。让咱们认真看看。
1 对 1 聊天的音讯表
图 12- 9 显示了 1 对 1 聊天的音讯表。主键是 message_id,它有助于确定音讯程序。咱们不能依附 created_at 来决定音讯序列,因为能够同时创立两条音讯。
群组聊天的音讯表
图 12-10 显示了群组聊天的音讯表。复合主键是(通道 id、音讯 id)。频道和组在这里示意雷同的含意。channel_id 是分区键,因为群组聊天中的所有查问都在一个频道中运行。
音讯 ID
如何生成音讯 id 是一个值得探讨的乏味话题。音讯 id 负责确保音讯的程序。要确定音讯的程序,音讯 id 必须满足以下两个要求:
•ID 必须是惟一的。
•ID 应按工夫进行排序,这意味着新行的 ID 高于旧行。
咱们如何能力实现这两个保障?首先想到的是 MySql 中的“auto_increment”关键字。然而,NoSQL 数据库通常不提供这样的性能。
第二种办法是应用全局 64 位序列号生成器,如 Snowflake[6]。这将在“第 7 章:在分布式系统中设计惟一的 ID 生成器”中探讨。
最初一种办法是应用本地序列号生成器。本地意味着 ID 仅在组中是惟一的。本地 ID 工作的起因是在一对一通道或组通道内保护音讯序列就足够了。与全局 ID 实现相比,这种办法更容易实现。
第 3 步 - 深度设计
在零碎设计面试中,通常冀望您深刻理解高级设计中的一些组件。对于聊天零碎,服务发现、音讯流和在线 / 离线指标值得深刻摸索。
服务发现
服务发现的次要作用是依据地理位置、服务器容量等规范为客户端举荐最佳的聊天服务器。Apache Zookeeper[7]是一种风行的服务发现开源解决方案。它注册所有可用的聊天服务器,并依据预约义的规范为客户端抉择最佳的聊天服务器。
图 12-11 显示了服务发现(Zookeeper)的工作原理。
- 用户 A 尝试登录应用程序。
- 负载平衡器将登录申请发送到 API 服务器。
- 在后端对用户进行身份验证后,服务发现会为用户 A 找到最佳的聊天服务器。在本例中,抉择了服务器 2,并将服务器信息返回给用户 A。
- 用户 A 通过 WebSocket 连贯到聊天服务器 2。
音讯流
理解聊天零碎的端到端流程很乏味。在本节中,咱们将探讨 1 对 1 聊天流、跨多个设施的音讯同步以及群组聊天流。
1 对 1 聊天音讯流
图 12-12 解释了当用户 A 向用户 B 发送音讯时会产生什么。
1. 用户 A 向聊天服务器 1 发送聊天信息 \2. 聊天服务器 1 从 ID 生成器获取音讯 ID\3. 聊天服务器 1 将音讯发送到音讯同步队列 \4. 音讯存储在键值存储中。5.a。如果用户 B 在线,则音讯将转发到用户 B 连贯的聊天服务器 2。5.b。如果用户 B 处于脱机状态,则会从推送告诉(PN)服务器发送推送告诉 \6. 聊天服务器 2 将音讯转发给用户 B。用户 B 和聊天服务器 2 之间存在长久的 WebSocket 连贯。
跨多个设施的音讯同步
许多用户有多台设施。咱们将解释如何跨多个设施同步音讯。图 12-13 显示了音讯同步的示例。
在图 12-13 中,用户 A 有两个设施:电话和笔记本电脑。当用户 A 用手机登录聊天应用程序时,它会与聊天服务器 1 建设 WebSocket 连贯。相似地,笔记本电脑和聊天服务器 1 之间存在连贯。
每个设施都保护一个名为 cur_max_message_id 的变量,该变量跟踪设施上的最新消息 id。满足以下两个条件的音讯被视为新闻音讯:
•收件人 ID 等于以后登录的用户 ID。
•键值存储中的音讯 ID 大于 cur_max_Message_ID。
因为每个设施上都有不同的 cur_max_message_id,音讯同步很容易,因为每个设施都能够从 KV 存储中获取新音讯。
群聊音讯流
与一对一聊天相比,群组聊天的逻辑更加简单。图 12-14 和 12-15 解释了流程。
图 12-14 解释了用户 A 在群聊中发送音讯时产生的状况。假如组中有 3 个成员(用户 A、用户 B 和用户 C)。首先,将来自用户 A 的邮件复制到每个组成员的邮件同步队列:一个用于用户 B,另一个用于用户 C。您能够将邮件同步队列视为收件人的收件箱。此设计抉择实用于小团体聊天,因为:
•它简化了邮件同步流程,因为每个客户端只需查看本人的收件箱即可取得新邮件。
•当组号较小时,在每个收件人的收件箱中存储一份正本并不太低廉。
微信采纳了相似的办法,它将一个群组的成员限度在 500 人[8]。然而,对于具备大量用户的组,为每个成员存储音讯正本是不可承受的。
在收件人端,收件人能够接管来自多个用户的音讯。每个收件人都有一个收件箱(邮件同步队列),其中蕴含来自不同发件人的邮件。图 12-15 阐明了设计。
在线状态
在线状态指示器是许多聊天应用程序的基本功能。通常,您能够在用户的个人资料图片或用户名旁边看到一个绿点。本节解释了幕后产生的事件。
在高级设计中,状态服务器负责管理在线状态并通过 WebSocket 与客户端通信。有一些流将触发联机状态更改。让咱们逐个检查一下。
用户登录
“服务发现”局部解释了用户登录流程。在客户端和实时服务之间建设 WebSocket 连贯后,用户 a 的在线状态和工夫戳处的最初一次流动将保留在 KV 存储中。状态指示器显示用户登录后处于联机状态。
用户登记
当用户登记时,它将通过用户登记流程,如图 12-17 所示。KV 商店中的联机状态更改为脱机。状态指示器显示用户处于脱机状态。
用户断开连接
咱们都心愿咱们的互联网连贯是统一和牢靠的。然而,状况并非总是如此;因而,咱们必须在设计中解决这个问题。当用户断开与 internet 的连贯时,客户端和服务器之间的长久连贯将失落。解决用户断开连接的一种简略办法是将用户标记为脱机,并在从新建设连贯时将状态更改为联机。然而,这种办法有一个次要缺点。用户通常会在短时间内频繁断开和从新连贯到 internet。例如,当用户通过隧道时,能够关上和敞开网络连接。在每次断开 / 从新连贯时更新联机状态会使状态指示器频繁更改,从而导致用户体验不佳。
咱们引入心跳机制来解决这个问题。在线客户端定期向状态服务器发送心跳事件。如果状态服务器在特定工夫内(例如 x 秒)从客户端接管到心跳事件,则认为用户处于联机状态。否则,它将处于脱机状态。
在图 12-18 中,客户端每 5 秒向服务器发送一次心跳事件。发送 3 个心跳事件后,客户端断开连接,并且在 x =30 秒内未从新连贯(任意抉择此数字以演示逻辑)。联机状态更改为脱机。
用户在线状态
用户 A 的敌人如何晓得状态更改?图 12-19 解释了其工作原理。状态服务器应用公布 - 订阅模型,其中每个好友对保护一个通道。当用户 A 的联机状态更改时,它将事件公布到三个频道,即频道 A -B、A- C 和 A -D。这三个频道别离由用户 B、C 和 D 订阅。因而,敌人很容易取得在线状态更新。客户端和服务器之间的通信是通过实时 WebSocket 进行的。
上述设计对于小用户群是无效的。例如,微信采纳了相似的办法,因为其用户群下限为 500。对于较大的群体,告诉所有成员在线状态既低廉又耗时。假如一个组有 100000 名成员。每次状态更改将生成 100000 个事件。要解决性能瓶颈,一个可能的解决方案是仅当用户进入组或手动刷新好友列表时获取联机状态。
第 4 步 - 技术总结
在本章中,咱们介绍了一个聊天零碎体系结构,它反对 1 对 1 聊天和群聊。WebSocket 用于客户端和服务器之间的实时通信。聊天零碎蕴含以下组件:用于实时消息传递的聊天服务器、用于治理在线状态的状态服务器、用于发送推送告诉的推送告诉服务器、用于聊天历史持久性的键值存储以及用于其余性能的 API 服务器。
如果你在面试完结时有多余的工夫,上面是一些额定的谈话要点:
•扩大聊天应用程序以反对照片和视频等媒体文件。媒体文件的大小显著大于文本。压缩、云存储和缩略图是值得探讨的乏味话题。
•端到端加密。Whatsapp 反对对音讯进行端到端加密。只有发件人和收件人能力浏览邮件。感兴趣的读者应参考参考资料 [9] 中的文章。
•在客户端缓存音讯能够无效缩小客户端和服务器之间的数据传输。
•缩短加载工夫。Slack 构建了一个地理分布的网络,以缓存用户的数据、通道等,从而缩短加载工夫[10]。
•错误处理。
–聊天服务器谬误。与聊天服务器的连贯可能有几十万个,甚至更多的长久连贯。如果聊天服务器脱机,服务发现(Zookeeper)将为客户端提供一个新的聊天服务器,以建设新的连贯。
–音讯从新发送机制。重试和排队是从新发送音讯的罕用技术。
OpenIM 理解咱们
OpenIMgithub 开源地址:
https://github.com/OpenIMSDK/…
OpenIM 官网: https://www.rentsoft.cn
OpenIM 官方论坛:https://forum.rentsoft.cn/
更多技术文章:
开源 OpenIM:高性能、可伸缩、易扩大的即时通讯架构
https://forum.rentsoft.cn/thr…
【OpenIM 原创】简略轻松入门 一文解说 WebRTC 实现 1 对 1 音视频通信原理
https://forum.rentsoft.cn/thr…
【OpenIM 原创】开源 OpenIM:轻量、高效、实时、牢靠、低成本的音讯模型
https://forum.rentsoft.cn/thr…