Socket.io和RethinkDB changefeed的比较

19

目前我正在使用不带RethinkDB的socket.io:

客户端向socket.io发出事件,socket.io接收事件,将其发给其他客户端,并保存到数据库以供持久化。新连接的客户端将从数据库中获取现有数据,然后通过socket.io监听新事件。

如果我切换到使用RethinkDB和changefeed会有什么帮助呢?

我认为使用RethinkDB的方式是客户端可以执行POST(插入到RethinkDB),而不是直接向socket.io发出事件,然后socket.io会观察RethinkDB changefeed,并在接收到新数据时向所有客户端发出事件。

相对于我的当前方法,使用RethinkDB和changefeed的优点是什么?对我来说,它们似乎都能实现同样的事情,但我没有看到RethinkDB方法的明显优势,因为我要去访问数据库,而不是直接从socket.io服务器发出事件,这肯定会慢一些。

1个回答

44
首先,让我们澄清 socket.io 和 RethinkDB changefeeds 之间的关系。Socket.io 用于客户端(浏览器)和服务器(Node.js)之间的实时通信。RethinkDB changefeeds 则是让您的服务器(Node.js)监听数据库中的更改的方式。客户端无法直接与 RethinkDB 进行通信。
实时应用程序的一个非常典型的架构是让 RethinkDB changefeeds 订阅数据库中的更改,然后使用 socket.io 将这些更改传递给客户端。客户端通常也会发出消息,根据您的应用程序逻辑,可以将这些消息写入您的数据库。
是的,您可以通过 socket.io 向所有客户端发送所有消息,然后将这些消息写入数据库以进行持久化。这样做的确更快,但是这种方法有许多缺点。
1. 数据库作为唯一的数据源
最容易发现的问题是以下内容:
- 如果您的应用程序无法将某些内容写入数据库会发生什么? - 如果要插入数据库中的数据无效或重复怎么办?需要编写应用程序逻辑来处理吗? - 如果 Node.js 服务器在发送写查询之前关闭了会发生什么?
这些只是一些快速示例,在这些示例中,由于您的架构,您将丢失或使数据失去同步。强调一下,您将丢失数据,因为您的主要真相来源是存储在内存中的数据。您还可能会发现 Node.js 应用程序和数据库中的数据之间存在差异。
关键是,数据库应始终是您的唯一数据源,并且只有在数据写入磁盘后才应确认数据。否则,晚上怎么可能睡得安稳呢?
2. 高级查询
如果您只是通过 socket.io 将所有新消息从所有客户端传递给所有客户端,那么您现在必须在客户端中拥有一些非常复杂的逻辑,以过滤掉实际上重要的所有数据。请考虑通过网络传递大量无用数据的情况,而客户端实际上不会使用这些数据。另一种选择是编写一个发布/订阅系统,其中您订阅某些频道(或类似的东西),以过滤实际重要的数据。 RethinkDB通过提供自己的查询语言来解决这个问题,您可以将其附加到changefeed上。例如,如果客户端需要在我users表中年龄介于20至30岁之间,住在加州旧金山附近10英里,并且在最近6个月内购买了书籍的所有用户,则可以使用ReQL(RethinkDB的查询语言)来表达此内容,并可以为该查询设置changefeed,因此仅在相关更改时通知客户端。这比仅使用Socket.io和Node.js要难得多。
第三个问题是可扩展性。 RethinkDB解决的最后一个问题是,它是比仅将所有内容存储在内存中的解决方案(通过Socket.io和Node.js)更具可扩展性。因为RethinkDB从头开始构建为分布式,所以您可以拥有20多个RethinkDB节点集群,其中包含分片和副本。默认情况下,您编写的每个RethinkDB查询都是分布式的。现在,您可以拥有20多个其他无状态的Node.js节点,所有这些节点都正在侦听changefeed。因为数据库是真相的中央来源,所以这不是问题。另一种选择是限制自己只能使用一个服务器,拥有其他某些发布/订阅系统(例如基于Reddis构建的系统),仅轮询单个数据库......可能还有更多的例子,但您可以看到我的意思了。
我很乐意听到这是否回答了您的问题,以及我是否理解了您的想法。一开始很难理解如何构建应用程序,但对于大多数实时体系结构来说,这确实是一种优雅的解决方案。

1
非常有帮助 :)对于高级查询,考虑使用多房间聊天应用程序,将消息发送到房间中的客户端。 我不能只听一个 changefeed(所有聊天记录),并仅向该房间中的客户端发出吗?r.table('chat').changes().run(r.conn, function(err, cursor) { cursor.each(function(err, row) { io.to('room-' + row.new_val.id, row.new_val); // 1 room }); });... 因此,高级查询与此特定用例无关,因为它们涉及监视每个房间的一个 changefeed,这相当浪费。 我们也不需要在此处拥有“非常复杂的逻辑”来处理客户端。 - user3096484
如此高级的查询对于这个特定用例并不相关,因为它们将涉及观察每个房间的一个 changefeed,这是相当浪费的。实际上,changefeeds 很便宜,因此最好设置更具体的不同 changefeeds,而不是设置一个后来必须处理应用程序逻辑的 changefeed。 - Jorge Silva
2
“我不能只听一个changefeed(所有聊天),然后只向该房间的客户发出吗?” 是的,您也可以这样做。如果您只想要聊天消息的firehose,那就没问题。话虽如此,当您需要时,我建议立即切换到编写ReQL查询+ changefeeds,而不是将其添加到应用程序层。同样,changefeeds查询非常便宜,不必担心慷慨地打开它们。 - Jorge Silva
这很好知道(changefeeds 很便宜),但应用逻辑只是 io.to('room-' + row.new_val.id, row.new_val);,所以这一点也不困难。 - user3096484
这个答案解决了我很多疑惑,尽管我将在golang中使用websockets。 - abhinav
我更喜欢观看变更源的想法,而不是处理聊天室,因为当你需要处理实时加入和离开房间的逻辑时,聊天室会变得更加复杂。 - Fareed Alnamrouti

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接