使用EventSourcing(NodeJS,MongoDB,JSON)跨多个偶尔连接的客户端同步数据

我正面临着在服务器和多个客户端之间实现数据同步的问题。 我读了关于事件采购,我想用它来完成同步部分。

我知道这不是一个技术问题,更多的是一个概念问题。

我只是将所有事件直接发送到服务器,但客户端devise为不时脱机使用。

这是基本的概念: 视觉概念

服务器存储每个客户端应该知道的所有事件,它不重放这些事件来提供数据,因为主要目的是同步客户端之间的事件,使他们能够在本地重放所有事件。

客户端拥有一个JSON存储库,同时保存所有事件重build存储/同步事件中的所有不同集合。

由于客户端可以离线修改数据,因此保持一致的同步周期并不重要。 考虑到这一点,服务器应该在合并不同事件时处理冲突,并在发生冲突时询问特定用户。

所以,对我来说,主要的问题是要消除客户端和服务器之间的差异,以避免将所有事件发送到服务器。 我也遇到了同步过程的顺序问题:先推送更改,先更改推送更改?

我目前build立的是在服务器端默认的MongoDB实现,它隔离了所有查询中的特定用户组的所有文档(目前仅处理authentication和服务器端数据库工作)。 在客户端,我已经构build了一个NeDB存储的封装,使我能够拦截所有的查询操作来创build和pipe理每个查询的事件,同时保持默认的查询行为不变。 我还通过实现由客户端生成的自定义ID来补偿neDB和MongoDB的不同ID系统,这些ID是文档数据的一部分,因此重新创build数据库不会混淆ID(在同步时,这些ID应在所有客户端保持一致)。

事件格式如下所示:

{ type: 'create/update/remove', collection: 'CollectionIdentifier', target: ?ID, //The global custom ID of the document updated data: {}, //The inserted/updated data timestamp: '', creator: //Some way to identify the author of the change } 

为了节省客户端上的一些内存,我将在一定数量的事件中创build快照,以便完全重放所有事件将更有效率。

所以,为了缩小这个问题 :我能够在客户端重放事件,我也能够在客户端和服务器端创build并维护事件,在服务器端合并事件也不成问题。复制整个数据库与现有的工具不是一个选项,因为我只同步数据库的某些部分(甚至不是整个集合,因为文档分配不同的组,他们应该同步)。

但是我遇到的麻烦是

  • 确定同步时从客户端发送什么事件的过程(避免发送重复事件甚至所有事件)
  • 确定要发送回客户端的事件(避免发送重复事件,甚至是所有事件)
  • 同步事件的正确顺序(推/拉变化)

另一个问题我想问的是,是否将更新直接存储在类似修订版本的文档中效率更高?

如果我的问题不清楚,重复(我发现了一些问题,但他们没有帮助我在我的情况下)或缺less的东西,请留下评论,我会尽我所能保持简单,因为我已经只是写下所有的东西,可以帮助你理解这个概念。

提前致谢!

这是一个非常复杂的主题,但我会尝试某种forms的答案。

看到你的图的第一个反应就是考虑分布式数据库如何在它们之间复制数据,并在一个节点发生故障的情况下恢复。 这通常是通过闲聊来完成的。

八卦轮确保数据保持同步。 带有时间戳的修订版本保留在需求合并的两端,例如节点重新连接时,或者在给定时间间隔(通过套接字等发布批量更新)。

像Cassandra或Scylla这样的数据库引擎每轮合并使用3条消息。

示范:

节点A中的数据

 { id: 1, timestamp: 10, data: { foo: '84' } } { id: 2, timestamp: 12, data: { foo: '23' } } { id: 3, timestamp: 12, data: { foo: '22' } } 

节点B中的数据

 { id: 1, timestamp: 11, data: { foo: '50' } } { id: 2, timestamp: 11, data: { foo: '31' } } { id: 3, timestamp: 8, data: { foo: '32' } } 

第1步:SYN

它列出了所有文档的ID和最后一个插入时间戳(可以随意更改这些数据包的结构,这里我使用详细的JSON来更好地说明这个过程)

Node A -> Node B

 [ { id: 1, timestamp: 10 }, { id: 2, timestamp: 12 }, { id: 3, timestamp: 12 } ] 

第2步:确认

接收到这个数据包后,节点B比较接收到的时间戳和它自己的时间戳。 对于每个文档,如果时间戳较旧,只要将它放在ACK有效负载中,如果是较新的,则将其与数据一起放置。 如果时间戳是一样的,那就不要做了。

Node B -> Node A

 [ { id: 1, timestamp: 11, data: { foo: '50' } }, { id: 2, timestamp: 11 }, { id: 3, timestamp: 8 } ] 

第3步:ACK2

如果ACK数据被提供,则节点A更新它的文档,然后将最新数据发送到节点B,以用于没有提供ACK数据的那些数据。

Node A -> Node B

 [ { id: 2, timestamp: 12, data: { foo: '23' } }, { id: 3, timestamp: 12, data: { foo: '22' } } ] 

这样,两个节点现在都有最新的两种方式合并的数据(如果客户端脱机工作) – 无需发送所有文件。

在你的情况下,你的真实来源是你的服务器,但是你可以很容易地在你的客户端之间用WebRTC实现对等的闲话。

希望这有助于某种方式。

卡桑德拉培训video

锡拉解释

我认为避免所有事件顺序和重复问题的最佳解决scheme是使用pull方法。 通过这种方式,每个客户端都维护其最后导入的事件状态(例如跟踪器),并向服务器请求最后一个事件生成的事件。

一个有趣的问题将是检测业务不variables的突破。 为此,您可以在客户端上存储应用命令的日志,如果发生冲突(事件由其他客户端生成),则可以从命令日志中重试执行命令。 你需要这样做,因为一些命令在重新执行后不会成功。 例如,一个客户端在其他用户同时删除该文档之后保存一个文档。

Interesting Posts