如何实现在线Excel多人协作 |
您所在的位置:网站首页 › 如何制作在线excel › 如何实现在线Excel多人协作 |
引言:结合工作实践和自己的一些思考,今天和大家分享在线Excel的协作方案。 如果你对在线文档的主题感兴趣还可以看这两篇文章:如何实现多人协作的在线文档,在线Excel存储方案 场景多个用户同时操作一个Excel文件。 场景中的实体有:用户、Excel。其中用户又分为「拥有者」、「阅读者」、「协作者」 拥有者:创建Excel的用户 阅读者:可以查看Excel的用户 协作者:可以编辑Excel内容的用户 创建领域模型![]() 协作的关键过程有: 「用户打开Excel」 「用户编辑Excel」 「用户退出Excel」 「用户删除Excel」 在所有的关键过程中,既需要客户端往服务端发送消息,也需要服务端往其他客户端广播消息。而且当用户频繁修改Excel内容时,为了保证每个人修改的内容实时同步到其他客户端,会有频繁的网络传输。这很像一个聊天室。在这种场景下长链接是比较合适的方案,「WebSocket」是实现长链接的常用方案之一。 和聊天室不同的是,聊天室更倾向于AP模型;在线Excel更倾向于CP模型,因为消息丢失或顺序不对,会导致文件内容错误,后果很严重。 以上这些关键过程的实现都需要知道一个Excel文件有多少人正在阅读、编辑。记录当前Excel的在线用户,才能在Excel内容变化时把变化的内容广播给他们。 Excel在线用户当前有「多少人在协作」是实时变化的数据,而且需要频繁、高效的访问,使用redis存储比较合适。我们可以使用redis的Hash类型存放,Excel的唯一ID作为Key,把在线用户、打开文件时间等信息存储起来。 hset excel_id user_id "打开时间"其他的存储类型,或redis的其他存储方式都是可以的。 状态广播WebSocket连接建立之后,客户端会和服务端的某一个副本「保持」长链接。用户打开Excel或者修改Excel内容,都需要根据当前excel_id,去redis中查找「在线用户」,然后发送「广播消息」,把状态变化同步到所有客户端。此场景下广播消息的发送有三种实现方案: 方案一:exce_id路由excel服务的所有请求,根据exce_id路由,这样同一个exce_id上的所有长链接都会在同一个副本上。需要发送广播消息时,当前exce_id的所有长链接都在此副本上,代码层面不用做任何特殊处理。 优点:实现简单,不侵入业务代码 缺点: 无法动态扩容,即使增加了副本,某个exce_id的请求还是打在原来副本上负载均衡不友好,如果在某个副本上exce_id的用户数都偏多,会导致单个副本链接数过多,其他副本可能会比较空闲如果一个Excel协作人数特别多,可能会导致副本cpu或内存被打满;换句话说,一个副本的上限决定了Excel能支持的同时在线人数无法抽离单独的WebSocket网关长链接本来就是有状态的,把服务的状态和副本绑定了,相当于把状态放大了方案二:事件广播需要发送广播消息时,Excel所有副本都根据exce_id从redis中获取在线用户,对比当前副本持有链接的Sessions中是否存在此用户信息。如果存在则向此链接发送广播消息,如果不存在就忽略不做处理。 有广播消息时对其他所有副本发送通知,可以采用消息队列来实现。让所有副本订阅某频道,有广播消息时,通过消息队列通知到其他副本。 除了消息队列还可以根据应用ID调用云平台的接口返回所有pod的VIP,然后根据VIP给所有副本发送请求。 建议采取消息队列的方案,减少对云平台的依赖。 优点: 可以动态扩容解耦Excel和副本不影响负载均衡可以有单独的网关层缺点: 需要引入消息队列,增加了系统的复杂性侵入业务逻辑,副本需要自己判断广播是否由自己发送导致很多对redis的无效请求,广播频繁发送会给线上环境带来较大的压力(因为一次广播并不一定牵扯到所有副本)方案三:注册中心,统一管理,指定发送由注册中心管理excel、用户以及副本的长链接关系,需要发送广播时,根据excel_id获取所有需要广播副本的vip/Host,调用其服务给客户端推送广播消息。 优点: 可以动态扩容解耦excel和副本不影响负载均衡可以有单独的网关层基本不侵入业务逻辑缺点: 需要引入注册中心,增加了系统的复杂性,增加了运维成本 ![]() 当某用户打开Excel时,需要同步此用户的信息到所有正在阅读或协作此文档的客户端。这时的交互流程如下。 用户在浏览器中打开Excel文件,并发送请求到服务端根据excel_id,在redis中查找所有在线用户如果没有找到数据,说明当前没有人打开此Excel,把自己插入redis中,执行完毕如果查找到数据,把自己添加到当前记录中给所有除自己外打开此文档的「链接」推送消息其他客户端接收到服务端的消息后,在页面上显示登录用户头像执行完毕![]() 用户对Excel的操作类型特别多,比如修改单元格内容、修改行宽、增加列、合并单元格等等。我们把用户对Excel的所有操作归为两类:1.「修改单元格内容」 2.「其他操作」 修改单元格内容对于修改单元格内容的操作我们采用互斥逻辑。互斥逻辑分为锁定、取消锁定、发送内容三部分。 锁定逻辑当用户选中某个单元格时,前端把选中信息发送到服务端服务端根据「excel_id和当前单元格坐标」取锁,取锁成功进行下一步;如果取锁失败,给当前用户返回此单元格正在被A用户编辑服务端根据excel_id获取当前在线用户,发起事件广播其他客户端收到广播消息后,在单元格右侧标识操作人的用户信息,同时禁止当前用户操作此单元格执行完毕![]() ![]() ![]() 如何判断取锁成功? 「excel_id和当前单元格坐标」不存在时说明没有用户操作此单元格,取锁成功。 「excel_id和当前单元格坐标」存在时,可以把用户ID当作锁的Value值,比较Value是否为当前用户,如果是也认为取锁成功,可以修改单元格内容。 加锁时设置默认超时时间,防止单元格内容被永远冻结。 此外还存在间隙问题:用户在客户端选中一个单元格后,“请求到服务端加锁,然后发送广播到其他客户端“ 的间隙时间较长,这中间如果有用户快速修改了同一个单元格的内容,会存在内容被覆盖 或者 修改失败两种风险。我们可以根据自己使用Excel的业务场景,决定允许当前状况发生,或者通过优化取锁逻辑来处理。 其他修改对于其他修改采用覆盖逻辑,时间靠后的操作,覆盖靠前的操作。 当用户选中某个单元格时,前端把选中信息发送到服务端服务端根据excel_id获取当前在线用户,发起事件广播客户端收到广播消息后,根据广播内容和当前表格内容重新渲染表格执行完毕采用覆盖逻辑的原因:用户的很多操作无法做合并。比如:A用户把单元格第一行高度由30px调整为50px;B用户把第一行高度由30px调整为40px。此时程序无法按照预期设置第一行单元格的高度 用户退出Excel当一个用户退出Excel时,需要同步这个人的信息到所有正在阅读或协作此文档的客户端。用户主动退出操作包含:点击页面左上角的回退按钮、浏览器的回退按钮、关闭浏览器等。还有可能因为异常的网络中断导致用户退出,所有的退出操作对应到服务端,就是WebSocket链接断开。可以采用WebSocket服务端的close事件当作用户退出的标识。交互流程如下: 服务端WebScoket断开,触发close事件服务端根据excel_id获取当前在线用户,如果没有找到数据,说明当前没有人打开此文档,删除redis中的在线用户记录,执行完毕;如果查找到数据,把自己从「在线用户列表」中删除,执行下一步给所有除自己外打开此文档的链接推送消息客户端接收到服务端的消息后,在页面上「在线用户显示列表」中,删除此用户或者标记为下线状态执行完毕用户删除Excel客户端发起删除请求服务端验证删除权限是否通过,通过继续执行,不通过返回没有权限根据excel_id,在redis中查找所有在线用户。如果没有找到数据,说明当前没有人打开此文档,删除redis中的记录,执行完毕如果查找到数据,给所有除自己外打开此文档的链接推送消息,客户端根据消息给用户弹框提示,excel已被删除执行完毕存在的问题此方案并没有解决协作中的所有问题,除了上文中已经提出的注意事项外,还有很多地方要注意。比如:遇到合并函数操作时,如何解决多个人操作的冲突?有人在修改一个单元格时,别的用户有合并单元格操作时如何处理?多个人同时修改一个单元格的逻辑能否优化? 消息传输层的问题尤其重要,需要单独说一下: 因为WebSocket消息是无序的,所以,以上场景依赖消息顺序时,都需要额外的保障机制WebSocket发送消息有可能失败,在服务端和客户端通信时,是否需要ACK机制?如果建立了ACK机制,握手的另一方正好下线了如何处理?链接异常断开又重新建立时,如何保证当前用户数据更新到最新状态?总结今天详细和大家介绍了,在线Excel协作的一些实现方案和关键流程,希望能起到抛砖引玉的作用。喜欢在线协作的同学可以一起来交流讨论。 |
今日新闻 |
推荐新闻 |
CopyRight 2018-2019 办公设备维修网 版权所有 豫ICP备15022753号-3 |