基于 geohashes 的 pubsub 主题划分的建议,以实现巧妙的 websocket 连接服务
Posted
技术标签:
【中文标题】基于 geohashes 的 pubsub 主题划分的建议,以实现巧妙的 websocket 连接服务【英文标题】:Advice on pubsub topic division based on geohashes for ably websocket connection service 【发布时间】:2018-11-13 21:24:56 【问题描述】:我的问题涉及以下用例:
用例参与者
用户A:设置广播区域并查看带直播帖子的流的用户。 用户B:在用户A设置的广播区域内第一个发送广播消息的用户。 用户C:在用户A设置的广播区域内发送广播消息的第二个用户。用例说明
用户 A 选择一个广播区域,他希望在该广播区域的边界(半径)内接收直播消息。 用户 A 打开直播并请求一组初始直播项目。 用户 B 从用户 A 的广播区域内广播一条消息,而用户 A 的直播仍处于打开状态。 用户 A 的 livefeed 打开时,带有 1 个新 livefeed 项目的标签会显示在其顶部。 当用户 C 从用户 A 的选定广播区域内发布另一个直播帖子时,标签计数器会增加。用户 A 收到类似于以下 Facebook 示例的通知:
我想应用的解决方案(我认为 Pubnub 使用)是为每个 geohash 创建一个主题。 在我的情况下,这意味着对于广播消息的每个用户,它都需要发布到 geohash-topic,如果它落在范围内,客户端(应用程序/网站用户)将通过 websocket 使用 geohash-topic定义的区域(半径)。 Ably 似乎使用 Web 套接字提供这种可扩展的服务。
我想它会简化成这样:
因此,这意味着需要从发送广播消息的当前位置提取 geohash。这个 geohash 应该具有足够小的粒度,以便接收用户可以设置或多或少准确的广播区域。 (即,如果我们希望允许用户定义接收实时消息的广播区域,geohash 应该具有足够的准确性,这意味着如果我们决定扩展,人们应该期望有相当多的主题)。
选项 2 是为粒度较小(覆盖较大区域)的 geohash 创建主题,并让客户端根据与消息一起发送的 latlng 值来处理准确性。 然后客户端将决定是否丢弃消息。然而,这意味着发送更多消息(更多开销)和更高成本。
我没有使用这种架构的经验,并且质疑这种方法的可行性/可扩展性。 您能否想到这个问题的替代解决方案以达到预期的结果或提供更多关于如何整体解决此类问题的见解? (我也考虑过使用常规的 req-res 流,但这意味着向服务器发送垃圾邮件,这似乎也不是一个很好的解决方案)。
我确实检查过。 给定一个 161.4 km² 的区域(如布鲁塞尔区域),geohashes 按字符串长度划分如下:
1 ≤ 5,000km × 5,000km
2 ≤ 1,250km × 625km
3 ≤ 156km × 156km
4 ≤ 39.1km × 19.5km
5 ≤ 4.89km × 4.89km
6 ≤ 1.22km × 0.61km
7 ≤ 153m × 153m
8 ≤ 38.2m × 19.1m
9 ≤ 4.77m × 4.77m
10 ≤ 1.19m × 0.596m
11 ≤ 149mm × 149mm
12 ≤ 37.2mm × 18.6mm
鉴于我们将允许用户可能有高达 153m 的不准确性(在用户可能希望订阅以接收本地广播消息的区域),它需要的主题数量肯定已经太大而无法仅覆盖整个布鲁塞尔地区。 所以我目前仍然有点卡在这个水平上。
【问题讨论】:
【参考方案1】:1.小酒馆
PubNub 是目前唯一通过 websockets 提供开箱即用 geohash pub-sub 解决方案的服务,但它们的价格非常高(500 台连接设备的成本约为 49 美元,20k 台设备的成本为 799 美元)更新: PubNub 已更新价格,现在无限设备。网站更新即将推出。
Pubnub 正在研究他们的定价模型,因为他们的一些客户为意外的流量高峰付出了高昂的代价。
但是,对于旨在对所有人开放的通用广播消息应用程序而言,这将不是一个可行的解决方案,因此流量非常难以预测。
很遗憾,否则这项服务对我们来说是完美的解决方案。
2。干练
Ably 提供了一个 pubsub 系统,用于通过 websocket 将数据流式传输到客户端,用于自定义通道。当客户端附加自己以发布或订阅该频道时,频道是动态创建的。
这里的主要问题是:
如果我们想要高geohash精度,我们需要大量的通道,因此我们必须付出更多; 如果我们使用低 geohash 精度,将会有很多冗余消息: 假设我们采用一个由 4 个字符的 geohash 表示的频道,跨越 39.1 x 19.5 公里的地理区域。发送到该频道的任何帖子都将被多路复用给该区域内当前正在收听的每个人。
但是,假设我们的应用允许的最大半径为 10 公里,而一半的连接用户将其设置为 1 公里半径。
这意味着 2 公里半径之外的所有帖子都将不必要地多路复用给这些用户,并且将被丢弃而不再使用。
我们还应该考虑这种方法的可扩展性。对于生产者或消费者需要的每个 geohash,将创建另一个通道。
与仅需要基于主题的主题的应用相比,拥有一个需要基于全球地理哈希的主题的应用肯定更昂贵。
也就是说,在全球范围内采用时,主题的数量会急剧增加,因此价格也会增加。
另一个考虑因素是我们的应用需要额外数量的频道:
通过 geohash 和组:我们的应用程序允许创建基于地理位置的组(这相当于 Twitter,如 #hashtags)。 按地点 被关注的用户(高级功能)尽管如此,这种方法仍有一些乐观的考虑:
仅当新闻源处于活动状态时才需要流式传输: 当用户在我们的网站上打开浏览器窗口时 + 当用户在移动设备上并主动打开相关的提要时 可以进行进一步优化,例如仅从 10 开始流式传输 刷新提要后 20 秒 根据当前活动,按地点/关注的用户进行流式传输可能会有高流量,但许多地点频道也将处于空闲状态在这方面一个非常重要的注意事项是 Ably 如何向其消费者收费,这可以充分利用我们的优势:
当以下任何一种情况发生时,通道就会打开:
通过 REST 在通道上发布消息 一个实时客户端连接到通道。该通道在客户端连接到该通道的整个过程中保持活动状态,因此 如果您连接到 Ably,附加到频道并发布消息,但 永远不要分离通道,通道将保持活动状态 因为该连接保持打开状态。一个打开的通道会在所有的通道自动关闭 以下条件适用:
没有更多的实时客户端连接到频道至少 距离上一条消息发布已经过去了两分钟。我们保持 频道存活两分钟,以确保我们可以提供 作为我们连接状态恢复的一部分,通道上的连续性。
例如,如果您有 10,000 个用户,并且在您最忙的时候 当月有一个高峰,500 名客户建立了一个 与 Ably 的实时连接,每个都附加到一个独特的频道和 一个全局共享通道,通道的峰值数量将是 每个客户 500 个独特频道的总和和一个全球共享的频道 通道,即 501 个峰值通道。如果整个月中的每一个 10,000 名用户连接并附加到他们自己独特的频道,但 不一定同时,那么这不会影响你的高峰 channel count as peak channels 是并发的通道数 在该月的任何时间点开放。
乐观结论
最重要的结论是,我们应该考虑到这个功能可能并不像人们认为它对于应用程序的第一版那样重要。
虽然 Twitter、Facebook 等提供了接收实时更新的功能(并且用户已经开始期待它),但我们应用程序的有限规模的初始测试版可以在没有的情况下运行,即用户必须刷新才能接收新的更新。
在应用首次启动期间,可以收集统计数据以更深入地了解详细的用户行为。这将使我们能够基于事实数据建立更可靠的基础设施和财务反思。
【讨论】:
在您的第一篇文章中,您对高度的局部性做出了假设,ie. 更精细的分辨率,这可能需要您的用户在第二篇文章中重新考虑post - 如果您切换到最近邻算法来确定频道参与度,这有关系吗?也许您的用户只是想要陪伴而不关心人们实际上离他们有多远 (显然是“输入”提交!?)无论如何,我还要说我遇到了一个商业 websocket 服务,它允许在数千个同行之间以每美元的价格发送大量消息月 - 没有提出建议(因为我实际上没有在商业上使用此服务)wsninja.io 及其定价:1000 个连接,每天 1000 万条消息,4.95 美元/月或无限连接,每秒 1K 条消息,29.95 美元/月 @brynk 实际上距离很重要。从这个意义上说,目标是城市,而实时步行距离是一个假设。例如,当您在附近实时看到某些东西时,您可能不想步行 15 公里到达目的地。我去看看链接,非常感谢。在某个时间点(未在一个月内汇总)有 1000 个连接可能是合理的。我想这取决于用户行为,我们要等到发布后才能知道。 是的,感谢您的澄清 - 还有另一个问题:用户是否需要了解自上次连接以来的历史消息,或者这只是实时消息?并且,您是否期望基于位置的过滤和主题过滤之间存在任何偏差(即 - 逻辑“标签”与 pubsub 队列概念“主题”不同) - 或者,当您谈论组时,您是在谈论“用户组? @brynk 使用“组”,您可以说“布鲁塞尔的沙发冲浪”。这就像一个标签。尽管实时提要是主要提要,但我没有真正的偏见。再说一次,这些团体是商业模式的一部分。所以这不是一个真正的选择。【参考方案2】:抛开 Ably、Pubnub 和 DIY 解决方案的问题,问题的核心是这样的:
消息过滤发生在哪里?
有三种可能的解决方案:
Pub/Sub 服务。
服务器(WebSocket 连接处理程序)。
客户端(客户端的设备)。
由于这显然是一种面向移动设备的方法,客户端消息过滤非常粗鲁,因为它增加了客户端的数据消耗,而大部分数据可能无关紧要。
客户端过滤也会增加电池消耗,并可能导致客户端接受率降低。
这留下了发布/订阅过滤(频道名称/模式匹配)和服务器端过滤。
Pub/Sub 频道名称过滤
单个 pub/sub 服务服务于多个服务器(如果不是全部),使其成为非常昂贵的资源(相对于我们手头的资源)。
使用频道名称过滤消息是理想的 - 只要过滤成本低(使用与频道名称哈希映射完全匹配)。
但是,与精确的模式匹配相比,模式匹配(订阅名称不准确的频道时,例如 "users.*"
)非常广泛。
这意味着发布/订阅频道名称过滤不能用于过滤所有消息而不会使发布/订阅系统过载。
服务器端过滤
由于服务器接受 WebSocket 连接并在 WebSocket 和 pub/sub 服务之间建立桥梁,因此它处于过滤消息的理想位置。
但是,我们不希望服务器为每个连接处理所有客户端的所有消息,因为这是一种极端的重复工作。
混合解决方案
一个经典的解决方案是将地球划分为可管理的部分(每部分 1 平方公里需要 5.101 亿个唯一频道名称才能完全覆盖……但我建议忽略 70% 的海洋空间)。
繁忙的路段可能会被细分(纽约市可能需要每 250 平方米而不是 1 平方公里的路段)。
这允许发布者发布到确切的频道名称,而订阅者可以订阅确切的频道名称。
发布者可能需要发布到多个频道,订阅者可能需要订阅多个频道,具体取决于他们的确切位置和网格的边界。
此过滤方案将过滤很多,但不是全部。
服务器节点需要查看消息,查看其确切的地理位置并过滤消息,然后再决定是否应通过 WebSocket 连接将消息发送到客户端。
为什么选择混合解决方案?
这使系统可以相对轻松地扩展。
由于服务器节点(按设计)比发布/订阅服务便宜,它们可用于处理精确位置过滤(繁重的工作)。
同时,可以利用发布/订阅系统的力量来最小化服务器的工作量,过滤掉明显的不匹配。
Pubnub 与 Ably?
我不知道。我没有使用它们中的任何一个。我使用 Redis 并实现了我自己的 pub/sub 解决方案。
我认为它们都很棒,这完全取决于您的需求。
当涉及到定制或复杂的情况时,我个人更喜欢 DIY 方法。恕我直言,如果我要实现它,这似乎属于 DIY 类别。
【讨论】:
服务器可以过滤帖子,例如使用 Redis 地理空间,但是如果不通过某种地理哈希系统,您将如何让客户端订阅 pubsub 系统以接收基于地理位置的帖子流?您是否建议立即在我们的服务器中自己实现 websockets?使用外部服务的主要原因是避免需要以可承受的价格扩展 websockets 的痛苦(我们自己这样做的开销可能也不会免费)。 @Trace ,是的,我绝对建议您自己实现 WebSocket / SSE 连接。这可能是相对简单的工作,但它对 pub/sub 性能有巨大影响,并允许跨服务器分发工作。如果每个人都直接连接到 pub/sub 服务,它可能会使服务过载。考虑到每台服务器可能为数千个客户端提供服务,并且只使用少数 pub/sub 连接。这是一种更具可持续性和可扩展性的设计,更易于维护。 我目前在两个世界之间。我注意到 Stephen Blum 在我当前的解决方案中更新了一些关于 Pubnub 定价的文本。这将使使用 Pubnub 成为一个理想的解决方案,因为它们似乎已经提供了开箱即用的基于 geohash 的主题,并且可以管理可伸缩性/性能问题,因此我们不必处理这个问题。但我确实也可以根据请求通过 Web 套接字将流返回给客户端。我会给它一些时间考虑一下,谢谢! @Trace,不客气。作为分手的想法,我可能会考虑研究 Pubnub 的实现方式及其服务/功能的细节(或限制)。如果我的回答有帮助,请随时投票(如果这不是您想要的解决方案,则无需接受)。 是的,我要看看如何使用 Pubnub 完成它。仍然会有挑战,客户必须根据定义的半径订阅大量主题。也许这并不理想,或者我可以找到权衡。我会考虑的,谢谢!以上是关于基于 geohashes 的 pubsub 主题划分的建议,以实现巧妙的 websocket 连接服务的主要内容,如果未能解决你的问题,请参考以下文章
基于特定电子邮件 ID 的 Google Pubsub 订阅