Cocos官方提供了一套热更新的解决方案,但是实际使用的过程中没有办法完全满足我们的需求,我们提出了一套自己的解决方案。

背景

Cocos 官方提供了一套热更新的解决方案,但我们认为这套方案在以下方面不能完全适合我们的需求:

  1. 必须要在 Cocos 引擎启动之后才能够开始热更新流程,而我们有些业务场景需要支持在 Cocos 引擎未启动的时候就能够开始热更新流程;
  2. 文件下载效率低,官方提供的方案是在客户端本地对比本地的文件 manifest 和服务端的文件 manifest,找出其中的差异,然后再将差异的文件下载下来,并逐个校验,效率较低;
  3. 没有回退兜底策略,若本地进行热更新之后出现异常导致用户无法使用,无法回退到上一个版本;

基于这些问题,我们给出了自己的 Cocos 热更新解决方案 TinyCocosFix。
TinyCocosFix 是基于 native 端实现,因此能够在 Cocos 引擎未启动时就开始热更新流程;我们将文件 diff 的逻辑放在服务端,服务端直接将两个版本的 diff 文件压缩成 zip 包返回给客户端,然后客户端再针对 zip 包做校验,整个 diff 的过程比官方的方案更加高效;另外,我们还在本地做了历史版本管理,当新的热更新版本出现异常,导致用户无法使用时,可以启动兜底策略,本地回滚到上一个版本,尽最大可能保证用户体验。

可行性分析

根据 Cocos 的官方文档,https://docs.cocos.com/creator/manual/zh/advanced-topics/assets-manager.html
Cocos 所有的文件均通过 FileUtils 查找,而 FileUtils 会按照搜索路径的优先顺序查找文件,所以理论上,我们只需要将热更新的文件下载下来,放到指定的目录,并将该目录添加到 FileUtils 的搜索路径的前面,这样我们就可以实现热更新了。

经过 demo 测试,验证了我们的猜想。因此整个热更新的原理其实比较简单,客户端携带版本号等相关参数请求后台,后台收到App的请求之后判断是否有热更新,若没有,则返回无;若有,则返回热更新资源包。客户端下载完热更新资源之后,将对应的路径插入到 FileUtils 搜索路径当中,即完成了热更新的过程。

TinyCocosFix的总体设计

整个热更新的流程如下图所示,根据角色分为前端,后台和客户端;
当我们发布新的 App 版本时,需要告诉热更新管理平台,并上传当前 App 的资源包,这时候的资源包为 base 包;
当我们需要发布热更新的时候,此时需要上传热更新资源包,管理平台可以指定该热更新资源包能够更新到哪个App版本,并和之前的资源包生成 diff 包;
当对应版本的客户端请求后台时,便会返回相应的 diff 包给客户端。

1.后台

链式管理

管理后台使用链式的方式对 App 版本和热更新版本进行管理,具体如下图所示:
我们发布热更包的时候可以自由的指定这个热更包能够在哪个 App 版本上生效。
例如当我发布热更新1.0版本,指定在4.3.5和4.4.0上生效,则在其后追加1.0;
当发布2.0时,1.0自动过期;
当发布3.0,指定在4.4.0和4.4.5生效时,4.4.0的2.0热更包过期,4.3.5的2.0继续生效。

生成最近5个diff包

当我们在管理后台创建热更新包时,会找最近的5个热更包生成 diff 包,如下图所示:
App 版本4.4.0已经发布了6个热更新版本,当我们创建7.0热更新版本时,会自动找最近的5个版本生成 diff 包,
此时,如果客户端的App版本是4.4.0,热更新版本为2.0 - 6.0,则会直接返回对应的diff包;
如果客户端的App版本是4.4.0,热更新版本为1.0,则第一次请求,后台会返回没有热更包,并触发对应1.0和7.0的 diff 包生成逻辑,下一次请求就会返回对应的热更包。

蓝盾流水线创建App版本

后台提供了相应的接口,支持在流水线上自动创建热更新版本。

接口如下:

2.客户端

客户端的热更新流程如下:

热更新策略

我们定义了三种热更新策略

热更新策略 特点
强制更新 不允许取消,热更新包下载完成之后立即重启生效
推荐更新 允许取消和跳过
静默更新 后台下载,第二次启动生效,用户无感知

历史版本管理

TinyCocosFix 会在本地最多保留三个历史版本,并对外提供了回退到上一个版本的接口,业务方可根据自己的需求来定义触发回滚的逻辑。
如下图所示,为 ABCmouse 的回滚策略:
我们会在主要场景检测发生的异常,若在一分钟内发生超过50次异常,且本地发生过热更新,则触发回滚机制。
回滚完成之后 TinyCocosFix 内部会上报触发回滚的异常,并记录发生回滚的版本,若下次检测到相同的版本,则不会提示有热更新。

异常管理

TinyCocosFix 内部会对异常进行管理,主要分为热更新过程异常和业务方的热更包自身逻辑异常,具体的处理方式如下表所示:

异常 处理 备注
热更新过程异常 记录热更新失败版本,下次遇到相同版本直接跳过 如多次 MD5 校验失败
热更包自身逻辑异常 TinyCocosFix 提供回滚接口,业务方自定义错误阈值,触发回滚,回滚之后记录回滚的版本号,下次遇到相同版本跳过 兜底策略

断点续传

若热更新下载过程中进程突然被杀掉,下一次启动会从上次下载的地方继续下载。

3.热更管理平台

主界面

热更新管理平台界面如下:

目前管理平台包括四个部分:App 版本管理,热更包版本管理,灰度用户管理,开发者管理。
App 版本管理
创建App版本界面如下:
我们 App 每次发版时,需要在管理平台创建对应的App版本,这样发布热更新时,我们才能够指定热更新支持的版本,后台也能够根据每个版本生成 diff 包,我们需要提供 App 的版本号以及对应的资源包。
这一步可以在蓝盾上使用流水线自动实现。

热更包版本管理
创建热更包需要提供热更新的资源包,并选择热更新策略,在下一步可以选择热更新生效的App版本。

灰度用户管理
在这里配置的灰度用户适用于精确灰度,当我们的热更包还处于自测阶段时,只有灰度id才能够收到热更包。

开发者管理
只有拥有权限的用户才能够进入到管理平台。

热更新发布流程

我们规范了热更新的发布流程,如下图所示:

我们认为发布是一个很危险的操作,任何发布的热更包都必须要经过测试,因此新创建的热更包只能是灰度包;
精确灰度
管理后台有一个灰度账号管理,创建的精确灰度包能够匹配到这些账号,这一步主要用于我们内部测试;
批量灰度
当我们精确灰度没问题之后,便可以进行下一个步骤,批量灰度。目前支持尾号匹配和上传 excel 两种批量灰度规则。
尾号匹配,例如用户的id为12345678,尾号匹配为8或者78,均能匹配到该用户。有时我们发布热更包希望只有某些确定的用户能够收到,则可以使用上传 excel 的功能,excel 表格中包含所有批量灰度的用户。
发布
批量灰度一段时间之后,观察下热更新的成功率和失败率以及回滚率,如果没有问题,便可以发布了。
在批量灰度和发布期间均可以将热更包过期,一旦过期热更包立即失效,过期的热更包可以恢复到之前的状态。

4.上报统计

TinyCocosFix 会上报三类信息,具体如下
|类别|备注|
|–|–|
| 初始化上报 | 统计热更新版本存量用户|
| 热更新结果上报 | 统计热更新成功率和失败率|
| 回滚上报 | 统计回滚率|
上报结果统计界面如下:

5.和官方热更新方案对比

官方 TinyCocosFix
本地计算 diff 后台计算 diff
每次下载一个文件,单个校验 下载整个 diff 包,整体校验 md5
必须启动 Cocos 引擎 无需启动,native 端可完成热更新流程
无回滚策略 增加回滚策略,出现问题回滚到上个版本

下一步规划

1.权限控制

目前管理平台还未分角色做权限控制,下一步,我们将会针对不同的角色给出相应的权限,以下是初步的规划:


根据发布的不同阶段,我们将角色分为开发,测试和管理员。开发只拥有精确灰度的权限,用于开发自测;测试拥有精确灰度和批量灰度的权限;管理员拥有最高权限。
所以整个发布的流程应当如下图所示:
开发完成之后,使用精确灰度进行自测,自测没问题之后便交付给测试同学;
测试同学测试通过后,便可以开始进行批量灰度,灰度一段时间观察没问题之后,测试便可以向管理员申请发布;
管理员收到申请之后,经过审批,热更新正式全量发布。

2.完善监控和报警机制

每次发布新的热更新版本之后,每日邮件同步热更新结果,包括热更新成功率,失败率和回滚率以及最近三个热更版本的存量。
当热更新失败率超过一定的阈值,触发报警,阈值给定默认值,用户也可以设置。

3.正式环境和测试环境

目前管理后台并未区分正式环境和测试环境,ABCmouse 是通过 debug 和 release 环境,指定不同的 appId,以此来达到区分正式环境和测试环境的目的。
下一步,TinyCocosFix 将在 sdk 内部支持正式和测试环境,将正式和测试环境在物理上进行区隔。正式环境将会更加稳定,不会受到测试的影响。而在测试环境我们可以更加方便的测试,而不用担心影响到现网用户。

感谢

感谢一起参与设计和实现热更新方案的小伙伴们。
@josephpan(潘伟洲) @mariozheng(郑磊) @ezli(李剑飞) @aflextyang(杨波) @shinhachen(陈新华) @erikyi(衣文琦) @irisxxiao(肖湘) @xuyangfan(范旭阳)