websocket - 聊天类应用的订阅和发布架构的最佳实践
问题描述
我想知道在订阅更改并将其发布给用户方面存在哪些最佳实践。这是一个相当广泛且措辞模糊的问题。因此,请允许我用一个例子来详细说明这一点。
想象一下以下(简化的)类似聊天的应用程序:
- 用户打开应用程序并看到主屏幕。
- 在此主屏幕上,获取并显示聊天组列表。
- 每个聊天组都有一个用户(成员)列表。
- 用户可以查看此成员列表。
- 每个用户/成员至少有一个可用的名字。
- 用户可以在设置中更改其名称。
- 现在重要的部分是:当这个名字被改变时,每个正在查看成员列表的用户都应该实时看到名字的改变。
我的问题涉及最后一点。
让我们创建一些非常幼稚的伪代码来模拟这样的事情。
客户至少应该订阅一些东西。所以我们可以这样写:
subscribeToEvent("userChanged")
后端应该使用正确的数据发布到此事件。所以是这样的:
publishDataForEvent("userChanged", { userId: "9", name: "newname" } )
当然,这段代码有问题。订阅用户现在获取每个用户的所有事件。相反,它应该只接收它感兴趣的用户的事件(即它当前正在查看的成员列表)。
现在这是我想了解更多的问题。我可以想到几个解决方案:
方法 1 客户端订阅事件,并随其发送他当前正在查看的组的 id。比如像这样:
subscribeToEvent("userChanged", { groupId: "abc" })
因此,在后端,当用户更改其名称时,应该会发生以下情况:
- 获取用户的所有组 id
- 使用这些组 ID 发送事件
像这样的东西:
publishDataForEvent("userChanged", { userId: "9", name: "newname" }, { groupIds: ["abc", "def" })
由于用户订阅了 id 为“abc”的组,并且后端发布到多个组,包括“abc”,因此用户将收到该事件。
此方法的一个缺点是后端应始终获取正在更改的用户的所有组 ID。
方法二
与方法 1 相同。但我们将使用 userIds,而不是使用 groupIds。
subscribeToEvent("userChanged", { myUserId: "1" })
因此,在后端,当用户更改其名称时,应该会发生以下情况:
- 获取与用户相关的所有用户 ID(例如
friendIds
,基于他与之共享组的用户) - 使用这些发送事件
friendIds
像这样的东西:
publishDataForEvent("userChanged", { userId: "xyz", name: "newname" }, { friendIds: ["1", "2" })
这样做的一个优点是订阅可以更容易地重用。因此,用户不需要为他打开的每个组启动单独的订阅,因为他使用的是自己的userId
而不是groupId
.
这种方法的缺点是它(与方法 1 一样,但可能更糟)可能需要大量 id 才能将事件发布到。
方法三
这个有点不同。
在此方法中,客户端订阅多个 id。
一个例子:
在客户端,应用程序收集与当前用户相关的所有用户。例如,这可以通过收集当前查看组的所有用户 ID 来完成。
subscribeToEvent("userChanged", { friendIds: ["9", "10"] })
在后端,发布方法可以非常简单,如下所示:
publishDataForEvent("userChanged", { userId: "9", name: "newname" }, { userId: "9" } )
由于客户端订阅了用户 ID 为“9”的用户,因此在多个用户中,客户端将收到此事件。
这种方法的优点是后端发布方法可以相当简单。
这样做的缺点是客户端需要相当多的逻辑来订阅正确的用户。
我希望这些例子使问题更清楚。我觉得我在这里错过了一些东西。像,主要的聊天应用程序公司,不能以这些方式之一来做吗?我很想听听你对此的看法。
在旁注中,我使用 graphql 作为后端。但我认为这个问题足够笼统,不能让它发挥作用。
解决方案
- 用户可以在设置中更改其名称。
- 现在重要的部分是:当这个名字被改变时,每个正在查看成员列表的用户都应该实时看到名字的改变。
我假设用户可以通过 FORM 更改他的名字。该表单的内容将与 HTTP-Reuqest 一起发送到 backand 脚本,该脚本将在 DB 中进行更改,例如
update <table> set field=? where userid=?
首选
这将是该后端脚本连接到您的 Web 套接字服务器并发送类似消息的地方。
{ opcode:'broadcast', task:'namechange', newname='otto' ,userid='47111' }
服务器将广播给所有连接的客户端
{task:'namechange', newname='otto' ,userid='4711' }
所有与userid='4711'有关系的客户端现在都可以采取行动。
备选方案 1
如果您无法将后端脚本连接到 Web 套接字服务器,则客户端可能会{ opcode:'broadcast', task:'namechange', newname='otto' ,userid='47111' }
在 FORM 被传输到后端脚本之前发送。
这是不稳定的,因为如果后端出现任何问题,虚假消息已经传递,或者客户端可能在消息发出之前就死了,那么没有人会注意到变化。
推荐阅读
- html - Outlook 模板的 HTML/CSS 格式 - 表格/行/单元格高度不起作用
- c++ - 类似的解决方案但不同的答案
- pdf - 使用 pdftk 从 PDF 解压附件时保留目录结构?
- python-3.x - Scrapy从csv或数组中抓取保存的链接
- unix - 从 jenkins 执行 shell 运行 ansible playbook
- javascript - fs.writeFile 在新创建的 vue 项目中不起作用
- asp.net-mvc - Handle Vue Route on Page Reload from MVC Controller
- azure-devops - CI/CD Deployment Using Azure Devops
- c++ - 在 Mac OS 10.15 中打开 GL 版本 2.1 而不是 4.1
- javascript - 将 Google Apps 脚本数组应用到 HTML 表单自动完成数据列表中