可扩展应用程序中的点对点消息传递?

Point to point messaging in scalabale application?

在谷歌搜索 sent/received 消息在 whatsapp 之类的聊天信使中的方式后,我发现他们使用基于队列的消息传递系统。我只是想 弄清楚这个功能的高级设计是什么

每个矿山的 HLD 理解:- 假设朋友 1 和朋友 2 在线。朋友 1 已与网络服务器 1 建立 HTTP 网络连接,朋友 2 已与网络服务器 2 建立 HTTP 网络连接。朋友 1 将消息发送给朋友 2。

现在一旦消息到达 Web 服务器 1,我需要将消息传送到 Web 服务器 2,以便消息可以通过已经建立的 Web 连接推送回朋友 2。

我相信这里可以使用分布式自定义 java 队列将消息从一台服务器传播到另一台服务器。一旦消息到达一个服务器,它将把它推到分布式队列(分布式队列因为负载平衡和高可用性)消息内容,fromUserId,toUserId。队列上将有一个侦听器,它将看到刚刚弹出消息的目标 userId,并找到哪个网络服务器目标 userId 处于活动状态。如果用户处于活动状态,则弹出消息并将其推送到客户端,否则将其存储在数据库中以便可以将其拉出 一次一次上线。要查看哪个用户在哪个服务器上处于活动状态,我们可以维护树状图,其中 userId 作为键,值作为 serverName 以进行高效查找

实际设计可能比上面的简要设计要多complex/scalable。 想知道这是否是可扩展聊天信使的正确方向?

此外,我相信对于这样一个可扩展的应用程序,我们需要有多个分布式队列而不是一个。 但是如果我们有多个分布式队列,系统将如何确保跨分布式队列的 FIFO 消息传递?

Would like to know if this is the right direction for scalable chat messenger?

使用消息队列设计此应用程序有以下好处:

  • 解耦 client-server 并减少故障爆炸:队列可以优雅地处理流量高峰,只需暂时增加队列大小,只要流量再次正常(或任何瞬态),队列就会恢复正常故障已修复)
  • 在消息传递应用程序中,客户端(手机)可以长时间离线。因此,同步设计将行不通,因为客户端可能无法访问消息传递。然而,对于消息队列的异步设计,消息传递的责任在客户端。因此,客户端一上线就可以轮询新消息。

所以,是的,这个设计在性能和可用性方面可以相当可扩展。唯一要记住的是,这种设计需要为每个用户提供一个单独的队列,因此队列的数量将与应用程序的用户数量成线性比例(这可能是一个重要的财务和可扩展性问题)。

But if we have multiple distributed queues how system will ensure the FIFO message delivery across distributed queues ?

许多队列,open-source(rabbitMQ、activeMQ)或商业队列(AWS SQS)都支持 FIFO 排序。但是,队列内部的 FIFO 保证是不够的,因为由于网络中的异步问题,单个客户端发送的消息可能会以不同的顺序传递到队列(除非您使用单个 not-distributed 队列和保证有序交付的 TCP)。

但是,您可以在客户端实现 FIFO 排序。按照这种方法,消息将包含一个时间戳,每个客户端在接收消息时将使用该时间戳对消息进行排序。唯一的 side-effect 是客户端可以看到一条消息,而无需首先看到所有以前的消息。但是,当以前的消息到达时,它们将以正确的顺序显示在客户端 UI 中,因此最终用户将以正确的顺序看到所有消息。

 Would like to know if this is the right direction for scalable chat messenger?

我可能更喜欢稍微不同的方法。你的想法是正确的,但我想补充一点。几年前我碰巧创建了这样一个聊天信使,它应该与 watsapp 非常相似。我敢肯定,当您搜索时,您会遇到 XMPP 可扩展消息传递和状态协议。我们使用 openfire 作为维护连接的服务器。您在

中解释的概念
Say Friend 1 and Friend 2 are online . Friend 1 has established HTTP web connection to web server 1 and Friend 2 has established HTTP web connection to web server 2. Friend 1 send the message to Friend 2.

称为federation,openfire可以运行采用federated模式。阅读完您的评论后,我发现每个用户点都有一个队列。我相信您已经知道这种方法不可扩展,因为它非常耗费资源。一个好的方法是使用 Actor 框架,例如 akka。每个演员就像 java 中的一个轻量级线程,每个演员都有一个收件箱。因此在这种情况下会处理消息传递。

所以你的场景转换为朋友 1 打开一个到 openfire xmpp 服务器的连接并初始化朋友 1 Actor.When 他输入一条消息,它被转移到朋友 1 演员的 in-box(每个演员在 akka 中有一个内存收件箱)。这会与 xmpp 服务器通信。该服务器有自己的数据库,并且由于它与其他 xmpp 服务器联合,它会尝试查找朋友 2 是否在线。 xmpp 服务器会将消息保存在其数据库中,直到朋友 2 上线。一旦朋友 2 与任何 xmpp 服务器建立连接,就会创建朋友 2 actor,并将其存在传播到所有其他服务器,xmpp 服务器 1 将通知朋友 2 的 actor。朋友 2 的演员收件箱现在会收到消息

可选:还有送货回执选项。 Friend2 阅读消息后,可以向朋友 1 发送送达回执以指示消息的状态,即已读、未读、已送达、未送达等。