钉钉协同引擎与应用场景技术探索

1,716 阅读8分钟

作者:孙然(煮虾)

协同引擎是钉钉面向跨平台终端并解决分布式数据一致性的 Runtime 与开发框架。

它主要解决三类场景问题:

  • 多人实时协同
  • 跨端同步与应用状态接力
  • 离线可用 Local First

同时,它也是面向开放的。一方产品可以用,三方开发者(小程序、H5 应用)也可以用。

协同引擎Demo Show

多人实时协同:一起标注

从钉钉客户端 6.0.0 开始,消息会话里的图片新增了“一起标注”功能。群成员可以对一张图片进行同时勾画,并且所有人都能看到其它人的实时笔迹:

在这里,协同引擎 SDK 提供了一套 CoCanvas(协同画板)组件,图片消息通过引用 CoCanvas 组件快速组装出多人协同能力。除了解决多人实时数据(笔迹数据)的协同问题,协同引擎还提供了当前正在协同的参与者信息,用于业务实现展示诸如“多少人正在标注”信息的功能。

跨端同步:聊天草稿

你是否有遇到过这样的场景:手机上编辑到一半的东西,回到电脑前想继续编辑?或者,手机上截了个图,要在电脑上继续编辑。现在看来,你得在手机上先把信息或图片发给自己,然后再在电脑里下载下来继续操作。整个流程会被跨端传输过程打断。

如果用协同引擎,整个流程或许可以变成这样——你可以实现端到端的输入同步:

或者在一端复制,直接在另一端粘贴:

实现手机端和电脑端一系列连贯操作的无缝衔接!

在上面的例子中,协同引擎提供了 CoString、CoMap、CoList 等基础数据结构,CoImage、CoClipBoard 等组件,让上层业务共享数据就像读写本地变量一样简单。业务只需要关心业务本身需要处理哪些数据,而无需关心跨端需要额外做些什么。未来协同引擎也可以继续提供诸如 CoCamera、CoAlbum 等高阶组件,让桌面端应用可以实现拉起手机相册选图、拉起手机相机拍照等更高级的功能。

跨端同步,是让同一用户的多台物理设备,变成一台统一的逻辑设备,共享数据、共享系统设备、共享应用状态。由于协同引擎本身已经具有了最基本的数据同步功能,所以也能够轻松支持这些需求场景。

面向开放:三方应用如何实现跨端接力

协同引擎作为跨平台底层引擎同时也赋能与三方应用,助力实现:

  • 一次开发多端运行:作为三方应用跨端协同的载体,开发一套代码多端运行(移动/桌面)的小程序
  • 桌面端大屏生产力:桌面端小程序自动适配为大屏模式,充分发挥桌面端差异化大屏生产力优势
  • 多人协同:基于协同引擎,实现多人任务协作
  • 跨端接力:基于协同引擎,实现跨端应用接力:手机上应用操作状态,自动接力到桌面端大屏打开
  • 离线可用:Local First。基于协同引擎,数据都在本地,任务亦可离线提交,协同引擎会确保任务最终成功
  • Backend As A Service:所有数据(同步、协同、接力)基于协同引擎,开发者只需写前端代码,无须开发服务端

这里面协同引擎做了什么?

  • 作为小程序数据层,Backend As A Service
  • 多人编辑操作的数据实时协同
  • 跨端数据同步,应用状态迁移接力

协同引擎简介

协同引擎是【面向跨平台终端的】【解决分布式数据一致性的】【Runtime 与开发框架】。

解决的问题

协同引擎主要解决三类场景问题:

  • 多人实时协同
  • 跨端同步与应用状态接力
  • 离线可用 Local First

以上三类问题,即便不使用协同引擎,也有其他实现方式。但这些实现方式对于业务方而言,要处理很多“协同”领域的底层工程问题乃至算法问题,例如网络处理、本地数据存储、长时间离线处理、多人实时操作合并、回滚与冲突解决等。

协同引擎作为开发框架,提供了通用的协同能力,并以协同变量/组件的接入方式解决上述问题:

  • 声明式使用数据。业务方直接使用协同变量,就像使用本地变量一样,可以无须关注底层的操作合并、回滚、冲突解决等问题。
  • 跨平台实现,全平台可用。让各端(Android/iOS/Windows/Mac/端外web)、各种形态(Native/H5/小程序)的应用都可以使用协同能力。

API与基本概念

协同引擎在上层的开发框架中提供了一套协同数据结构和 API。它们和普通的容器变量相似,但是却自带了协同能力。例如一个云协同的计数器就可以这样通过 CoCounter 协同变量解决:

Container container = Loader.getContainer(url);
CoCounter counter = container.getCoCounter("my_counter");

// 点击+1按钮时
counter.add();

// 监听数据变化
counter.addEventListener(new CoCounterValueChangedListener() {
    @Override
    public void onValueChanged(int newValue) {
        // 更新UI
        updateView(newValue);
    }
});

这里,我们提出了几个基本的概念

CDS

Collaborative Data Structure,协同数据结构。一个 CDS 对象即一个协同数据变量。

CDS 也有不同的类型,例如 CoString、CoBoolean、CoMap、CoList,顾名思义对应了编程语言中常用的基础数据类型及集合类型。

当然也有更“复杂”的数据类型,例如 CoText(支持 OT 的文本类型)、CoPixel(图形标注类型)。

Container

一组 CDS 对象的集合。同一个 Container 中的 CDS 对象可以互相引用,例如 CoList 中的某个元素是 Container 中另一个 CoString 对象。

开发者只需要像使用自己熟悉的数据结构一样用协同变量开发业务,就能轻松实现数据协同。

内部架构1客户端协同引擎客户端自上而下主要分为以下几层:Platform:各平台提供面向开发者的 API 层API:跨平台胶水层Loader:负责加载 Container 的单例模块Container:协同引擎跨平台模块核心,负责 CDS 管理和各项引擎运行时必要流程Service:协同引擎依赖的平台层能力接口Provider:各平台实现 Service 层接口的能力2服务端
协同引擎服务端的职责相对少一些,主要负责连接和会话的管理、消息广播、定序和落库,同时也会有操作变换。

更多用武之地

除了 Demo Show 中的应用场景,还有更多的可能性……

更多的“一起XXX”

除了已有的“一起标注”,还有很多诸如“一起玩”、“一起听”、“一起看”等等更多的“一起XXX”的场景。在这些场景中,开发者所遇到的数据协同相关的问题都会有很多交集,想必都能用一套协同引擎来解决。

弱网离线优化

这个话题看似与协同关系不大,但协同引擎自带的 Local First 属性已经保障了弱网和离线下的可用性。许多弱网离线优化需要做的事情,协同引擎已经做过了。

那么协同引擎是如何做到弱网离线可用的呢?

离线可用性

协同引擎内含一个缓存队列——PendingQueue,它负责缓存所有服务端未确认的本地操作(op)。当业务操作协同数据结构时,会产生一个操作,协同引擎首先会将其缓存在 PendingQueue 中并落库。PendingQueue 中的操作会按先来后到的顺序依次发送,直到操作被服务端确认后才会发送下一个。在每次协同引擎初始化时,也会将数 PendingQueue 从本地数据库恢复。所以在协同引擎中,数据的产生和恢复,都是经历先本地落库、再发送等待服务端确认这个过程的。这就是所谓的 Local First,保证了协同引擎的离线可用性。

弱网可用性

首先,协同引擎会感知网络连通性。当明确网络不连通时,会暂停 PendingQueue 的发送操作;网络连通后会再自动开启发送机制。

其次,在网络连通时,不论出于什么原因,只要发送的操作在一定时间内(如 5 秒内)如果没有收到服务端的确认,协同引擎就会重新发送这个操作,直至服务端确认为止。这就保障了协同引擎在弱网下的可用性。

对比

下面的表格对比了弱网离线优化相关工作与协同引擎中已有的一些机制:

可以看出,大多在弱网离线优化中需要做的事情,协同引擎都已经具备相应能力了。协同引擎最初的设计已经符合了弱网离线可用的主要要求,并且保障操作永不丢失,亦可用于弱网离线优化的相关工作中。

总结

本文介绍了协同引擎框架,它致力于解决多人多端数据协同的问题,让业务轻松实现多人协作的功能。我们列举了几个直观的 Demo,然后探讨了一些其它应用场景,除了能够应用于各种”一起XXX“的业务,还能用于解决跨端数据接力问题和弱网离线优化工作。

关注【阿里巴巴移动技术】微信公众号,每周 3 篇移动技术实践&干货给你思考!