浅析版本灰度
2023/08/31
背景
当产品需求多,迭代速度快,在安卓和iOS开发等客户端领域,用客户端 native 开发的方式,难以满足快速更新、快速覆盖的要求。
- 首先,新版本需要双端开发,开发人力成本高;
- 其次,版本审核流程长,时间久,会大大影响需求迭代的节奏。
因此,往往会借鉴或借用Web技术能力,超越客户端的版本限制。最简便的办法,用 webview 承载部分性能要求不高的需求,安卓和iOS只需开发一份,无需双端开发,降低了人力成本。同时可以通过下发JS包,动态更新,实现所谓的动态化功能,超越了客户端的版本限制,规避了客户端 native 开发的缺点。还可以更进一步,充分利用 native 的能力,使用 React Native 或 Flutter 等动态化架构,既能动态更新,也有相对不错的性能。
无论用 Webview 还是动态化架构,一般都需要下发脚本代码,如JavaScript包。以此规避客户端版本问题。
这种方式更灵活,规避了客户端版本更新的限制。这种灵活性,是一把双刃剑,既能快速迭代支持需求,也容易造成线上问题。对客户端封装的接口的不当调用,下发的配置或参数问题,引发客户端crash,时有发生。
因此,就需要慎重考虑版本管理问题,规避线上大的风险。推而广之,在其他场景,也需要控制风险。
用途
金丝雀(Canary)发布,来自以前矿工为了辨别矿井是否有毒气,随身携带一只金丝雀。金丝雀对毒气敏感,遇到毒气,会先挂掉,从而有预警作用。灰度发布,本质上也是一种风险控制,以较小的代价来试错。
灰度有时候不仅仅用于控制风险,还可以用于新功能的实验,AB test 之类。
无论是用于控制风险,还是新功能实验,都需要数据来支持,一般灰度以后,需要有配套的监控上报,以及相关的告警策略。
常见方案
简陋的方案
在较简陋情况下,只需要两个版本,线上正在运行的版本,以及正在开发中的版本。后者开发完成以后,覆盖线上的版本,就可以完成新版本发布。一旦线上出问题了,就回退到上一个版本。版本支持回溯。然后再开发一个修复的版本,去覆盖线上版本。
这种简陋的方案,容易出现问题。新的版本覆盖就版本需要一个过程,在覆盖过程中,用户本地依然是有问题的旧版本,直到达到的更新条件。例如,客户端拉JavaScript一般有时机限制。为了不影响启动性能,这种更新文件的操作,一般会放到启动以后某个合适的时间点。这样会影响新版本的覆盖率。在一些情况下,可能不能满足要求。例如,不符合国家的合规,需要立即处理的。
仔细考察一下,这里的版本覆盖,有精细的含义。这里把版本生效与否,与版本覆盖,合二为一了。二者需要分离。为了让二者区别更为明显,我们把把静态的代码包,从后台,移到用户本地。这样用户本地,可能存在多个版本。那个版本生效,就需要有一个规则来控制。我们可以把这份规则,在后台配置好,下发到客户端。每次客户端启动的时候,就需要去拉去这份配置文件。然后,读取配置文件中的版本规则,命中的版本生效。
配置文件的较小,可以即时拉取。这样,不同版本的静态资源包,与版本生效功能就分离了。更进一步,配置文件还可以用来管理版本,比如某一个版本有严重问题,可以通过配置屏蔽掉。上面的合规场景,此时我们只需要利用配置文件的屏蔽版本的能力,直接屏蔽掉即可。
这样还存在一个问题,页面依赖静态资源包,一旦被屏蔽了,需要有兜底逻辑。兜底可以根据场景,合理选择,用一个默认页面,或者跳转到其他页面,或者弹一个用户提示。
精细的方案
版本生效,和版本的静态资源包覆盖,应该分离。前者应该用可以即时拉取的配置文件管理。不依赖后者的是否成功下载到本地。这样版本生效,就和静态资源的拉取逻辑解耦了。
我们存储了的历史版本的代码包,以及新的代码包。通过配置来管理生效的版本。以上方案中,新的版本和历史版本,可以放到一根数轴上,然后用一个生效的版本指针,指向某一个版本。这里是一维的,简洁有效。
但我们可能会遇到更复杂的场景,新的版本不只有一个。比如,上一个正在灰度的版本尚未全量,新的版本就要开始灰度。或者为了修复线上有问题的版本,修复的版本尚在灰度中,新的版也要灰度了。以上场景中,当前要灰度的版本,不只有一个,而是多个。横向一维的版本管理,添加一个纵向纬度的版本管理,一横一纵,就是一个二维版本平面。
此时当前的新一系列版本,就需要有优先级管理。此场景也体现了,通过版本配置文件来管理生效版本的好处。没有这种分离,新版怎么处理呢?最新拉取的覆盖前面的新版本么。一旦有不同的场景,需要用到最新的包,和次新的包,这种方案就难以处理。
版本灰度要素
一般来说,B端(客户端)的数据消费,需要考虑变更的时效性。而C端(后台配置端)的数据处理,需要保证配置的正确和稳定。版本管理需要2大功能:
- 可恢复性:一旦有线上故障,可以快速回滚版本;
- 可追溯性:每个版本有记录,回滚的时候,可以快速查看不同版本的差异;
分桶规则
新版本的灰度,所谓灰度就需要依赖有标识来控制影响范围。一般来说, 灰度平台制定策略,针对不同人到到群,不同入口等等,下发不同的版本。 常见的有:服务器ip白名单,模运算分桶,正则匹配,用户cookie,url参数,ip,uid。
灰度策略
定义好分桶规则以后,就需要处理分桶规则和版本号的关系。特别是在多个灰度版本共存的情况下,分桶规则和灰度版本号映射关系,还需要配置好优先级。
策略路由
有了分桶规则、灰度策略,就可以根据业务请求所携带信息,通过策略路由,将二者连接起来。这里实际是一个 BFF 层,实际项目中,需要考虑一下带来的性能成本,需要关注页面的 TTFB(Time to first byte)时长数据。
阅读资料
log
- 2023/08/31 创建文档
- 2023/09/01 初稿