如何实现分布式消息同步?

2024 年 11 月 1 日
 molvqingtai

前段时间写了一个浏览器扩展,详见: https://v2ex.ih06.com/t/1076581?p=1#reply25

有一个痛点,因为所有消息都储存在本地,导致无法接收离线消息,例如:

现在有 UserA 、UserB 、UserC 、UserD 4 个用户,A 、B 、C 在线,D 离线

时间点 1:UserA 、UserB 、UserC 三个用户聊天,产生 3 条信息 “message-1-A, message-1-B, message-1-C”

时间点 2:UserC 离线退出聊天,UserA 与 UserB 继续聊天,产生 2 条信息:“message-2-A, message-2-B”

时间点 3:UserC 、UserD 上线加入聊天,那么此时,时间点 1 和时间点 2 的聊天信息需要同步给 UserC 和 User D

UserA 、UserB 拥有所有信息记录 5 条, 无需同步

UserC 本地记录 3 条,需要同步时间点 2 ,同步 2 条

UserD 本地无记录,需要同步时间点 1+2 同步 5 条

如上,我了解一些分布式同步的解决方案,etcd 、raft 等,奈何太菜看得一脸懵逼,上手太复杂 目前想到的解决方案就是,A 、B 、C 、D 广播自己所有的消息记录,然后各自接收,通过消息的时间戳 Diff, 然后同步时根据 Diff 出的差值,追加或插入。

  1. 性能问题:是否可以避免广播所有的消息记录,能否做到只广播其他用户需要同步的记录?
  2. 同步信息过多:因为插件的消息时储存在浏览器 IndexDB 中,一方面是有大小限制,感觉没有必要同步所有的历史消息,比如可以设置一个只同步 7 天 30 天..的聊天记录,或许有更好的方案?
  3. 消息数据结构:我现在只是简单的使用一个带时间戳的 List 来存储消息,要实现上面的功能是否有更好的存储方案,比如链表等?
4067 次点击
所在节点    程序员
38 条回复
jimmy2024
2024 年 11 月 1 日
又来一个套免费方案的
xichuhanguguan
2024 年 11 月 1 日
我有个疑问,像 p2p 用的 DHT 网络。他在加入网络的时候是需要一个知道一个在网络中的节点,向他去获取网络中其他节点信息。新用户怎么加入这个网络,邀请制吗?
molvqingtai
2024 年 11 月 1 日
@jimmy2024 我的应用本来就是开源免费的,单纯的技术交流,何来免费套方案
molvqingtai
2024 年 11 月 1 日
@xichuhanguguan 同一个域名就是节点,一个域名一个聊天室
Dynesshely
2024 年 11 月 1 日
不引入服务器的话这个问题无解
提前下线的用户,在无人在线的情况下登录,无法获取既有的聊天数据
KagurazakaNyaa
2024 年 11 月 1 日
@xichuhanguguan 要么通过 tracker ,要么有 boostrap 节点
molvqingtai
2024 年 11 月 1 日
@Dynesshely 没人在线的情况下,获取聊天记录也没意义
seedhk
2024 年 11 月 1 日
想到一个点:
没有服务器的情况下,如果新节点上线,如何保证他获取到的消息是未经过篡改的?
molvqingtai
2024 年 11 月 1 日
@seedhk 不能保证,能做到的只能验证消息的数据格式,不符合格式就过滤掉
molvqingtai
2024 年 11 月 1 日
@seedhk 我一直没有实现文件传输功能,这也是考虑的点,怕大家电脑中毒了
Dynesshely
2024 年 11 月 1 日
@molvqingtai 你要解决的不就是接收不了离线消息的问题嘛
molvqingtai
2024 年 11 月 1 日
@Dynesshely #31 可能我描述不太准确,应该是可以同步其他客户端本地存在的消息,但自己本地不存在的消息,要实现这个操作当然需要至少两个用户在线
bli22ard
2024 年 11 月 1 日
看过电报的群组聊天协议, 每条消息有一条 id ,这个 id 就是从 0 开始递增,每次+1 , 发消息,服务端每次+1 就可以了。对于客户端来说,它只要关注,群组 id 和 最后一条消息的 id 。客户端启动,建立 websocket 连接,带上自己的 last message id , 然后服务端,从这个 last id 开是这个客户端推送,这个同步要考虑,消息差了非常多,你需要设定一个阈值,超过这个阈值,就只取最后多少条消息。这块有个细节就是,要处理好,websocket 建立这段时间,出现新的消息,而导致的一些倒霉客户端没同步到这些最新消息的问题。你说的 etcd 这些用来实现存储一致性的,不适合你这种场景,你这种场景 mysql 就可以了,性能不够分库,就可以了。
fano
2024 年 11 月 1 日
@DsuineGP 正解
2Nfree
2024 年 11 月 1 日
直接引入区块链吧,内容寻址、分块存储、P2P 传输、DHT 索引
2Nfree
2024 年 11 月 1 日
@2Nfree 但是这样还是解决不了全部节点下线消息不可用的问题
molvqingtai
2024 年 11 月 1 日
@2Nfree #35 准备综合楼上的思路手撸了
molvqingtai
2024 年 11 月 3 日
@all 感谢各位,目前已实现,逻辑如下:
同步最大消息为 30 天内的历史记录,使用最后一条消息作为判断依据
同步信息数量不一定是 30 天内所有的消息, 如果在同步之前,产生了新的时间点的信息,则不会同步


A,B,C,D,E 5 个用户,A ,B 在线,C,D,E 离线

A-B 聊天,产生信息两条 messageA, messageB

A-B 离线

C-D 上线,产生数据两条 messageC, messageD

A-B 上线,C-D 将会给 A-B 推送两条消息 messageC 和 messageD ,但是 A-B 不会给 C-D 推送 messageA 和 messageB ,因为 C-D 的最新消息时间点比 A-B 早

E 上线,A-B-C-D 均会给 E 推送消息 messageA, messageB, messageC, messageD

最终结果:
A-B 显示 4 条消息 messageC, messageD, messageA, messageB

C-D 显示 2 条消息 messageA, messageB

E 显示 4 条消息 messageA, messageB, messageC, messageD


如上:
C-D 没有同步到早于自己的消息
一方面是,如果要全量同步 30 天,必然需要根据 30 天内的消息时间点 Diff ,然后插入,现在的实现只是增量追加,而且消息随着时间累积会越来越多

先暂时这样,后续看看是否有必要将 30 天内的数据全量同步

这是一个专为移动设备优化的页面(即为了让你能够在 Google 搜索结果里秒开这个页面),如果你希望参与 V2EX 社区的讨论,你可以继续到 V2EX 上打开本讨论主题的完整版本。

https://v2ex.ih06.com/t/1085531

V2EX 是创意工作者们的社区,是一个分享自己正在做的有趣事物、交流想法,可以遇见新朋友甚至新机会的地方。

V2EX is a community of developers, designers and creative people.

© 2021 V2EX