关于im:即时通讯系统架构设计如何设计一款WhatsApp

88次阅读

共计 6221 个字符,预计需要花费 16 分钟才能阅读完成。

本文译自 tech takshila 经 OpenIM 技术人员整顿订正后公布。
写在后面

Open-IM 是由前微信技术专家打造的 开源 的即时通讯组件。Open-IM 包含 IM 服务端和客户端 SDK,实现了高性能、轻量级、易扩大等重要个性。开发者通过集成 Open-IM 组件,并私有化部署服务端,能够将即时通讯、实时网络能力疾速集成到本身利用中,并确保业务数据的安全性和私密性。

理解更多原创文章:

【OpenIM 原创】开源 OpenIM:轻量、高效、实时、牢靠、低成本的音讯模型

【OpenIM 原创】C/C++ 调用 golang 函数,golang 回调 C /C++ 函数

【OpenIM 原创】简略轻松入门 一文解说 WebRTC 实现 1 对 1 音视频通信原理

【OpenIM 扩大】OpenIM 服务发现和负载平衡 golang 插件:gRPC 接入 etcdv3

【开源 OpenIM】高性能、可伸缩、易扩大的即时通讯架构

如果您有趣味能够在文章结尾理解到更多对于咱们的信息,期待着与您的交换单干。

免责申明:这是设计 WhatsApp 等即时通讯零碎的一种办法。然而,咱们不能保障 WhatsApp 是以这种形式设计的。这是咱们设计相似零碎的想法。

问题陈说

设计一个即时通讯平台,如 WhatsApp 或 Signal,用户能够利用该平台互相发送音讯。应用程序的一个重要方面是聊天信息不会永恒存储在应用程序中。

乏味的事实:一些聊天信使(如 FB Messenger)存储聊天信息,除非用户明确删除它。然而,像 WhatsApp 这样的即时通讯工具不会将音讯永恒保留在服务器上。

零碎需要

instant messenger 应用程序应满足以下要求。

  • 它应该可能反对用户之间的一对一对话。
  • 它应该可能向其余用户显示发送 / 交付 / 读取确认。
  • 它应该可能提供对于用户上次流动工夫的信息。
  • 它应该容许用户共享图像。
  • 它应该可能向其余用户发送推送告诉。

容量布局

咱们须要建设一个高度可扩大的平台,可能反对 WhatsApp 规模的流量。此外,在进行容量布局时,咱们须要确保思考顶峰流量的最坏状况。上面列出了咱们能够用来估算应用程序容量的一些数字(如 WhatsApp)。

- 每月应用程序上的用户数:10 亿

- 顶峰流量时每秒的流动用户数:650000

- 顶峰流量时每秒的邮件数:4000 万

整个应用程序将由几个微服务组成,每个微服务执行特定的工作。解决聊天信息流量的数据立体(网站管理员包含到分布式系统章节的链接)(咱们称之为 chat microservice)中所需的服务器数量能够应用以下等式进行预计。

#𝑠𝑒𝑟𝑣𝑒𝑟𝑠 𝑖𝑛 𝑐ℎ𝑎𝑡 𝑚𝑖𝑐𝑟𝑜𝑠𝑒𝑟𝑣𝑖𝑐𝑒=(# 聊天信息 / 秒)__∗ 提早)/# 每台服务器的并发连贯

假如每台服务器的并发连接数为 100K,发送音讯的提早为 20 毫秒。在这种状况下,聊天服务器机队中所需的服务器的预计数量(应用上述等式)将为 8(即 4000 万 *20 毫秒 /100K)。在规范实际中,倡议再增加几个服务器,以解决这些服务器可能的故障。在接下来的一节中,咱们将看到这些聊天服务器对总体基础设施老本的影响。

乏味的事实:在本次演讲中,Rick Reed(软件工程师 @WhatsApp)谈到了优化基于 Erlang 的服务器应用程序,以及调整 FreeBSD 内核以反对每台服务器数百万个并发连贯。这在很大水平上帮忙了他们放弃尽可能小的服务器占用空间。

高级设计

此即时通讯应用程序所需的性能能够应用两种微服务建模:聊天服务和长期服务。聊天服务将为沉闷用户发送的在线聊天信息流量提供服务。该服务将查看接管音讯的用户是否在线。如果用户在线,则音讯将立刻转发给该用户。否则,音讯将由长期服务解决。此服务将负责保护发送给脱机用户的所有音讯(文本或图像)。数据将临时存储在长期存储器中,直到脱机用户复原联机。咱们将在前面的一节中提供无关各个组件的更多详细信息。

乏味的事实:WhatsApp 实际上应用了一种十分相似的办法,正如同一位 WhatsApp 工程师(Rick Reed)在另一次演讲中所探讨的。

API 设计

咱们能够公开一个 REST 端点来与聊天服务交互。上面介绍了用于发送音讯的 API 端点的定义。

sendMessage(String fromUser、String toUser、ClientMetaData ClientMetaData、String message)

申请:

_fromUser:\u 发送申请的用户 ID

_toUser:\u 向其发送申请的用户 ID

clientMetaData:用于存储客户端信息(如设施详细信息、地位等)的元数据。

音讯:作为通信的一部分发送的音讯。

数据模型

咱们将存储详细信息,例如用户连贯到的服务器以及用户上次流动的工夫。咱们能够应用基于文档的数据库(如 MongoDB)来存储用户信息,如用户的最初流动工夫(也称为心跳工夫)。数据模型将相似于下表。

用户 ID(香港)心跳工夫已连贯服务器

表 1:数据模型 - 用户信息

组件图

在本节中,咱们将探讨在一对一通信中发送音讯的两种不同场景。之后,咱们将探讨须要反对的其余性能,例如推送告诉和用户活动状态。最初,咱们将钻研进行优化和解决故障场景的不同机制。

一对一通信

这里,咱们将探讨与向另一个用户发送音讯相干的两种不同场景。第一种状况波及向在线用户发送文本音讯。在第二个场景中,咱们形容了向脱机用户发送图像所波及的操作序列。

场景 1:向在线用户发送文本

上面介绍了向在线用户发送文本音讯的序列图中每个步骤的详细信息。

  • 步骤 1:Alice 向 Bob 发送一条音讯,该音讯被定向到 Alice 连贯的聊天服务器。
  • 步骤 2:Alice 从其连贯的聊天服务器(即聊天服务器 A)取得确认,音讯标记为 Alice 端发送。
  • 步骤 3:聊天服务器向数据存储器发出请求,以获取无关 Bob 连贯的聊天服务器的信息。
  • 步骤 4:聊天室存储返回 Bob 连贯到聊天室服务器的信息。
  • 步骤 5:聊天室服务器 A 将音讯转发给聊天室服务器 B。
  • 步骤 6:应用推送机制将消息传递给 Bob。
  • 步骤 7:Bob 将 ACK 发送回聊天服务器。
  • 步骤 8:ACK 被转发到 Alice 连贯的聊天服务器 A。
  • 步骤 9:ACK 被传递给 Alice,并被标记为已传递。
  • 步骤 10:当 Bob 浏览音讯时(假如 15 分钟后),另一个 ACK 被发送到 Chat_Server_B。
  • 步骤 11:聊天室服务器申请获取 Alice 连贯的服务器。
  • 步骤 12:聊天室存储返回 Alice 连贯到聊天室服务器的信息。
  • 步骤 13:聊天室服务器 B 将读取确认转发给聊天室服务器 A。
  • 步骤 14:将 ACK 转发给 Alice,将申请标记为已读。

场景 2:向脱机用户发送媒体文件

上面介绍了序列图中向脱机用户发送图像的每个步骤的详细信息。

  • 步骤 1:Alice 向 Bob 发送一个图像,该图像被转发到聊天服务器 A,Alice 与之连贯的服务器。
  • 步骤 2:聊天服务器将图像上传到文件服务器,文件存储在目录构造中
  • 步骤 3:文件服务器将上传文件的图像 url 返回给聊天室服务器。
  • 步骤 4:图像 url 返回给 Alice,用于在 Alice 的设施上出现图像。图像被标记为在 Alice 端发送。
  • 步骤 5:聊天服务器向 Bob 连贯的服务器发出请求。
  • 步骤 6:聊天室存储返回 Bob 离线的信息。
  • 步骤 7:聊天服务器将蕴含图像 url 的音讯转发给长期服务器
  • 步骤 8:长期服务器将蕴含图像 url 的音讯存储在长期存储器中。
  • 第九步:Bob 上线并与 Chat_Server_B 进行心跳(网站管理员包含链接)。
  • 步骤 10:聊天服务器从长期服务器获取 Bob 的长期音讯。
  • 步骤 11:聊天服务器将长期音讯转发给 Bob。
  • 步骤 12:Bob 从文件服务器获取图像。此时,映像将被传送到 Bob 的设施,所有对瞬态音讯的援用都将从零碎中删除。
  • 步骤 13:Bob 的设施向聊天服务器发送 Alice 图像的确认
  • 步骤 14:获取 Alice 连贯到的服务器的信息;i、e. 聊天室服务器
  • 步骤 15:将确认转发至聊天室服务器
  • 步骤 16:将 ACK 传递给 Alice,将音讯标记为已传递。

瞬态数据存储

咱们能够应用基于 FIFO 的策略实现基于队列的机制来存储和检索瞬态音讯。为此,咱们能够应用现有的基于云的技术,如 AmazonSQS 或 WindowsAzure 队列服务。咱们能够应用这些队列来存储发送给脱机用户的长期音讯。一旦消息传递给脱机用户,所有对这些长期音讯的援用都将从零碎中删除。

推送告诉

应用推送技术向用户传递音讯有两种办法:客户端推送或服务器推送。如果咱们沿着客户机申请的路线走上来,咱们能够在长轮询和短轮询之间做出决定。另一方面,有两种办法能够实现服务器推送办法:WebSocket 和服务器发送事件(SSE)。Websockets 曾经成为聊天应用程序事实上的通信协议。咱们在上面的局部中提供了无关它的更多详细信息。

应用轮询技术,客户机定期向服务器申请新数据。抉择轮询技术的衡量决定能够应用上面提到的数据点。

  • 短轮询:(例如,基于 AJAX 的计时器)
  • 长处:如果申请之间的工夫距离较长,则更简略且不太耗费服务器
  • 毛病:不适用于须要以最小提早告诉服务器事件的状况
  • 长轮询:(例如,基于 XHR 的 Comet)
  • 长处:服务器事件的告诉不会提早
  • 毛病:更简单,耗费更多服务器资源

将服务器音讯推送到客户端的办法次要有两种类型。第一种是 WebSocket,它是一种通信协议。它通过单个 TCP 连贯提供双工通信信道。因为双向通信,它非常适合聊天应用程序等场景。另一种称为服务器发送事件(SSE),它容许服务器在建设初始客户机 - 服务器连贯后异步向客户机发送“新数据”。SSE 更适宜发布者 - 订阅者模型,如实时流式股票价格;twitter 提供更新和浏览器告诉。

用户活动状态

用户最初一次处于活动状态是能够在即时消息中找到的规范性能。咱们在下面的表 1 中展现了存储相干信息的数据模型

在图 4 中,咱们展现了应用 WebSocket 在客户端和服务器之间保护连贯的机制。一旦在客户端和服务器之间建设了初始连贯,通信就会切换到双向二进制协定。客户端和服务器之间的连贯应用心跳放弃活动状态。咱们将上次从用户接管心跳的工夫存储在数据库中。而后能够查问数据存储以获取用户上次流动的工夫。

优化

咱们能够应用上面列出的参数来倡议零碎中的优化。在本文中,咱们提供了倡议系统优化所应遵循的办法的详细信息。

  • 提早:咱们能够应用分布式缓存(包含指向分布式缓存的链接),例如 Redis,在内存中缓存用户活动状态及其最近聊天的信息。这可能有助于缩小应用程序的总体提早,并提供更好的客户体验。甚至一些数据库解决方案也提供了内存缓存解决方案,如 Amazon DynamoDB Accelerator。
  • 基础设施老本:从零碎中能够显著看出,聊天服务器对基础设施老本的重要奉献。如果不管制聊天服务器的脚印,那么聊天服务器产生的老本可能会迅速减少。一种办法是减少每个主机的连接数。这将大大减少保护服务所需的服务器数量。咱们能够通过调整服务器配置和抉择适合的技术来实现这项工作。例如,WhatsApp 的工程师通过优化基于 Erlang 的服务器应用程序和调优 FreeBSD 内核,可能在每台主机上实现数百万个连贯
  • 可用性:咱们能够保护长期音讯的多个正本,这样即便其中一个正本中的音讯失落,也能够从另一个正本中检索。这意味着要保护这些长期音讯的正本。客户端将负责从两个队列获取音讯,并将它们合并。咱们将在下一节中探讨更多内容。

解决瓶颈

零碎中更容易产生故障的次要瓶颈是聊天服务器和长期存储解决方案。在上面的局部中,咱们举荐了一些解决此类故障的办法。

  • 聊天服务器故障:零碎中的聊天服务器将放弃与用户的连贯。有两种解决聊天服务器故障的办法。一种办法是将这些 TCP 连贯传输到另一台服务器;然而,这种故障转移的实现并非微不足道。第二个绝对简略的办法是让用户客户端在连贯失落的状况下主动启动连贯。用户连贯到的服务器的信息须要在数据库中更新,这与咱们采取的办法无关。例如,在图 5 中,咱们展现了解决此故障场景的示例。咱们能够看到 User1 连贯到 Server1,当该服务器敞开时,将从新建设与另一台服务器(即 Server2)的连贯,并在数据库中更新此信息。

  • 瞬态存储故障:瞬态存储是另一个容易产生故障的组件,可能会导致脱机用户在传输过程中失落音讯。咱们能够复制每个用户的长期存储,以避免在他们脱机时发送给他们的音讯失落。当用户从新联机时,将查问并合并用户长期存储的原始实例和正本实例。在图 6 中,咱们展现了一种解决刹时存储故障的机制,该故障在用户从新联机时启动。

监测

咱们心愿确保咱们的服务可能以高可用性和低提早满足用户需要。咱们能够为这些指标定义服务级别协定(SLA),并创立中度和重度监控器,当违反这些 SLA 时,这些监控器会触发警报。对于此应用程序,咱们能够为 sendMessage API 定义以下 SLA。

  • 可用性 SLA:p99.999
  • 提早 SLA:p99.99,共 5 毫秒

可用性 SLA 意味着,如果 1000 个申请中有 1 个以上失败,监控器将触发警报。同样,提早 SLA 意味着,如果服务器对其接管的 100 个申请中超过 1 个申请的响应工夫超过 5 毫秒,则会触发警报。

此外,咱们能够在不同的谬误场景中设置故障警报。当聊天服务器无奈从长期存储的所有正本中为用户获取长期音讯时,可能会呈现这种状况。这映射到下面图 3 所示的步骤 #10,其中聊天服务器#B 申请长期服务器在 Bob 脱机时获取发送给 Bob 的音讯。让咱们假如咱们在长期存储器中保护 Bob 音讯的两个正本,以使零碎更加强壮。然而,因为长期存储的长期问题,长期服务器无奈从两个正本检索音讯。这是一个须要调试的谬误场景,因而须要足够的监控警报。

扩大要求

咱们能够扩大零碎以反对群组聊天,通过群组聊天咱们能够将消息传递给多个用户。咱们能够创立一个数据模型来存储组数据实体,该实体将由 GroupChatID 标识,并将用于保护属于该组的人员列表。上述零碎可扩大以反对向在线和离线用户发送音讯的场景。咱们能够构建一个组件,该组件将负责确保依据组中所有用户的活动状态将消息传递给他们。

作为此问题的扩大,能够涵盖的另一个方面是安全性,特地是端到端加密,其中只有通信用户能够读取音讯。每个用户都有一个公钥,该公钥与该用户正在通信的所有其余用户共享。例如,两个用户 Alice 和 Bob 正在互相通信。Alice 领有 Bob 的公钥,反之亦然;然而,它们的私钥不共享。当 Alice 向 Bob 发送音讯时,该音讯应用 Bob 的公钥加密并通过网络发送。服务器将加密的音讯定向给 Bob,Bob 应用私钥解密音讯。这样,服务器只能拜访加密的音讯,只有 Alice 和 Bob 能力读取他们替换的理论音讯。

OpenIM github 开源地址:

https://github.com/OpenIMSDK/…

OpenIM 官网:https://www.rentsoft.cn

OpenIM 官方论坛:https://forum.rentsoft.cn/

咱们致力于通过开源模式,为寰球企业 / 开发者提供简略、易用、高效的 IM 服务和实时音视频通信能力,帮忙开发者升高我的项目的开发成本,并让开发者掌控业务的外围数据。

IM 作为外围业务数据,平安的重要性毋庸置疑,OpenIM 开源以及私有化部署让企业能更放心使用。

现在 IM 云服务商免费高企,如何让企业低成本、平安、牢靠接入 IM 服务,是 OpenIM 的历史使命,也是咱们后退的方向。

如您有技术下面的浅见请到咱们的论坛分割沟通,用户也可与咱们的技术人员谈讨应用方面的难题以及见解

正文完
 0