一种前端灰度发布方案

本文介绍一种前端灰度发布方案,主要解决的是传统的灰度发布只能以机器维度进行分组的问题。提供一种用户维度分组的灰度发布机制。前端

传统灰度发布,由于是以机器分组,因此要求服务是无状态的。所谓无状态就是对请求的处理是上下文无关的。有长链接、读写文件、缓存等场景,就是所谓”有状态“的。有状态的服务,若是用户的前一个请求打在机器A,后一个请求打在机器B,就会出问题。node

因此,有状态的服务灰度发布,要作到:webpack

  1. 同一用户始终访问同一版本的代码
  2. 放量过程像传统发布同样可控

本灰度发布方案对构建、部署、启动服务、处理请求阶段分别作改造,实现有状态服务灰度发布。git

方案概述

咱们把线上的代码称为stable版,本次发布的新代码称为beta版。先总体描述一下方案:web

  • 用git tag标识每次发布
  • 在构建阶段生成tag,同时用tag名称来命名manifest.json
  • 每次构建完新版本后,从cdn源机器取回上次发布的manifest.json文件,一并放在dist目录下
  • 部署阶段全量部署到全部机器,在运行阶段来决定访问哪一个版本的代码
  • node层启动服务时,读dist目录下的两份manifest.json文件,这样就能拿到新旧两个版本的文件清单
  • 处理请求时,根据动态配置的放量信息和分流策略,来决定使用哪一个manifest.json中的文件
  • 版本号信息放在cookie中,以保证同一用户始终访问同一版本代码

开发阶段

正常开发代码,无需有任何额外操做。算法

构建阶段

publish-tag

新增一个git tag,以p-开头,意为publish。每次发布都有一个tag标记,格式为p-201911111001-lvdabao.标记发布时间与发布者。构建完成并同步cdn成功后,会将该tag同步到git仓库。json

manifest.json

manifest.json是webpack构建完毕后的文件清单,能够用webpack-manifest-plugin插件生成。若有特殊需求也能够本身编写。咱们是本身编写,并在动态渲染首页HTML时读取清单内容并输出script标签。缓存

每次构建生成的文件名称是这样的格式:manifest-p-201911111001-lvdabao.json,这样每次发布都生成对应tag命名的manifest.json文件。cookie

启动服务时能够一次读取到内存中,并非处理每一个请求都读一下文件,因此没必要担忧性能。性能

获取上次发布的版本信息

咱们是用publish-tag来标识版本号的,只要拿到上次发布时的tag,就能取到对应的manifest.json文件。因此构建的最后一步就是把上一版的manifest.json文件从cdn源机器取到当前构建后的dist目录下,为后续服务启动时使用。

取上次的tag也很简单,一个git命令搞定:git tag --sort=-taggerdate | grep "^p-.*" | head -n 1

容错机制

若是上次发布的版本有重大问题,不能做为stable版使用,有什么办法呢?

因此咱们增长了一个额外流程,容许构建的时候传入环境变量,指定stable tag。这样在获取stable版本信息时,优先取环境变量中指定的。

部署阶段

部署跟普通流程没什么区别,将dist目录发布到目标机器就好了。每次部署的dist文件包含如下:

  • beta版的manifest.json文件
  • beta版的资源文件
  • stable版的manifest.json文件

由于stable版的资源文件已经在cdn了,因此本次部署没必要管他们。

启动服务

启动服务时咱们须要干两件事情:

  1. 把两个manifest.json文件读到内存中,供分流时使用
  2. 自动修改放量配置,将新版本比例改成0

放量配置

上面提到了放量配置,这个是放在单独的配置系统中的,固然简单点放在服务端也是能够的。用途就是根据当前用户的uuid,来肯定用户该使用哪一个版本的资源。
配置内容也及其简单:

{
    percent: 10
}

percent便是beta版的放量比例,10表示10%的用户使用beta版。全量的时候手动改成100就行啦。

由于启动服务的时候会自动将percent改成0,因此每次发布完后,咱们只需根据放量节奏逐步扩大percent的值就好。

处理请求

万事具有,咱们在处理请求的时候,就很easy了。只需获取当前用户的uuid,node层经过RPC调用获取到放量配置,经过分流策略来计算应该使用哪一个版本的资源。
咱们的首页是动态输出的(SSR),拿到分流策略得出的tag,把相应的manifest.json中的文件输出,这样就控制了哪部分用户使用beta版本。

分流策略

最后再谈谈分流策略,这块也是有不少细节的。分流策略要作的核心工做:

  1. 根据放量配置来决定当前用户应使用的资源版本
  2. 确保用户的分流路线稳定,即下次请求页面应与上次的分流结果一致
  3. 新版本发布或放量比例变化时,从新分流

首先,放量配置只有一个百分比数字,咱们须要把uuid散列化,即把uuid字符串对应到0-99间固定的数字。算法能够有不少,咱们选一种简单的,取每一个字符的ASCII码相加,而后再除100取余。伪代码:

for (i = 0; i < uuid.length; i++) {
    hash += uuid.charCodeAt(i);
}

取到的这个hash就能够与放量百分比比较,在范围内就使用beta版。

另一个比较麻烦的事情是第2点,为了让用户下次访问的时候可以跟首次的分流一致,咱们须要把首次分流的结果保存在cookie中。当请求来的时候先分析cookie中的版本信息,若是可用则优先用cookie。不可用的话清掉cookie,再去计算分流。

那么既然uuid的散列算法能保证hash值稳定,每次都用uuid计算不行吗?缘由就是咱们访问环境的特殊,uuid的稳定性不能保证,相对来讲仍是cookie更稳定。这个看项目吧,若是你的项目uuid稳定,那能够省去用cookie。

总结

以上就是灰度发布方案的核心内容啦,相关的代码细节不赘述,有读者朋友感兴趣能够留言探讨。
经过这套方案咱们实现了以用户维度进行分组的灰度发布,而且整个流程足够自动化,业务开发无感知,须要手动操做的只有修改放量配置。
有朋友可能想说,你这个方案和ABTest很类似啊!其实ABTest和本方案的差异主要有:

  1. 须要同时上线AB两套新代码
  2. 最终会丢弃其中一套代码

尽管看起来差异不大,但ABTest方案的构建、部署、分流控制都会有所区别。

固然,若是咱们把ABTest退一步,认为stable版是A,新上线代码是B,那么将本方案改形成ABTest方案也很容易。

相关文章
相关标签/搜索