灰度发布是互联网产品发布常用的一种方式,顾名思义,就是在黑和白(0和1)之间平滑过渡的一种产品发布方式。产品发布者根据某种规则,让一部分用户继续用原来的产品功能,另一部门用户开始逐渐启用新功能,在过渡的过程中可能还会对产品做进一步完善,灰度发布完成后,所有的用户都将使用新的产品功能。常见的灰度发布策略:
金丝雀发布 A/B Test
金丝雀发布一般先发 1 台,或者一个小比例,例如 2% 的服务器,主要做流量验证用,也称为金丝雀 (Canary) 测试(国内常称灰度测试)。以前旷工开矿下矿洞前,先会放一只金丝雀进去探是否有有毒气体,看金丝雀能否活下来,金丝雀发布由此得名。简单的金丝雀测试一般通过手工测试验证,复杂的金丝雀测试需要比较完善的监控基础设施配合,通过监控指标反馈,观察金丝雀的健康状况,作为后续发布或回退的依据。
AB测试是为Web或App界面或流程制作两个(A/B)或多个(A/B/n)版本,在同一时间维度,分别让组成成分相同(相似)的访客群组(目标人群)随机的访问这些版本,收集各群组的用户体验数据和业务数据,最后分析、评估出最好版本,正式采用。
互联网产品需要快速迭代开发上线,又要保证质量,保证刚上线的系统,一旦出现问题可以很快控制影响面,就需要设计一套灰度发布系统。
灰度发布可以根据配置,将用户的流量导到新上线的系统上,来快速验证新的功能,而一旦出问题,也可以马上的恢复,简单的说,就是一套A/B Test系统。
首先我们来看一下不是用开源框架,灰度发布系统架构如何实现,系统架构图如下:
上游服务接入客户端请求,根据下方的灰度配置将符合条件的请求转发到下游旧版本上 下游新旧服务是处理客户端请求的业务服务系统 灰度发布的策略由服务配置管理平台下发给上游服务
这里有两个细节需要注意,首先就是上游服务根据上面策略转发流量到新老版本服务,这个需要根据业务来自己定了,比如说用户ip对某个数取模。另外就是依据什么来区分新老服务,这里在在服务注册时候就需要把版本信息注册到服务注册中心,然后根据服务配置管理平台的灰度策略转发流量到下游服务的新老版本。
灰度发布系统架构图结合我们的应用架构设计可以演化一下,具体如下:
要自己实现灰度发布,我们肯定是需要在框架层面做一些设计和改造的,首先就是我们的请求,肯定是需要在数据协议上提前做好设计的:
定长Header 变长Body 设计初确定灰度发布字段 uid token sessionid ip tag
定长Header
{
uid
token
sessionid
ip
tag
cmd
bodylength
}
复制
变长Body
{
数据体
}
复制
灰度发布策略分两种:
单策略 uid/token/ip 基于上下文灰度策略 多个模块同时灰度 tag
比如我只有网关层做灰度发布,那这个时候其实只需要用到数据协议中的uid/token/ip这些字段即可。但是如果是基于上下文灰度策略需要同时对多个模块进行灰度,那就必须对请求打tag了,然后在灰度调用链的第一层对请求打tag,后面调用链上遇到有tag的请求转发到灰度服务,没有的转发的老板服务。比如我现在要对网关层和数据访问层同时进行灰度,那就需要在网关层对请求进行打tag处理,具体示意图如下:
意思就是打了tag的请求全部转发到灰度服务。其实你可以就使用基于上下文的策略,不用考虑单策略,因为现在的互联网应用基本上很少存在单策略灰度的场景了,业务需求下来基本上会牵涉到多个服务的变化。
在应用的各个层面为了灰度发布做框架代码的改造还相对容易,比如你web层框架使用的是现在非常流行的spring boot,那你去改造spring boot框架或者一些常用的rpc框架,基本上理解一些aop原理、动态代理和线程的知识就够了。但是在软负载层面,比如我们常见的nginx,你要去做改造那就麻烦很多了,因为你要懂c。但是好在现在nginx支持Lua这类脚本语言,那我们就可以在这上面做点文章了,我们可以扩展Lua脚本去实现我们的灰度策略。比如我们可以在nginx机器上面部署一些Agent接受服务配置管理平台下发的灰度发布策略,然后更新nginx的配置,最后优雅重启nginx服务,具体示意图如下:
Agent的实现也很简单,你就拿一个线程池(web框架)能接受请求,然后能调用一些系统命令就行了,就spring boot就行,如果要考虑效率,可以在spring boot启动的时候用netty之类的rpc框架启动长连接服务。
应用层面的灰度发布其实都好搞,只要提前做好设计,无非是想办法改造改造框架的问题,尤其在spring boot出来之后,这种微内核插件化的框架让你改造起来更得心应手,理解spring aop、java动态代理、常用设计模式和多线程知识最后加上starter搞定一切。但是一旦涉及到数据的灰度,就不是那么好搞了。因为涉及到数据的灰度我们要考虑2个问题:
灰度服务使用到的数据库要提前准备 灰度前后数据库的表字段不一致
一般情况下,在应用正常运行期间我们会把数据库从库拿下来进行灰度数据的准备,但是从库拿下来这段期间还是会有流量进来,所以为了保证数据全量复制灰度的上一层必须做双写的处理,即老版本的请求流量也需要往灰度的服务写,灰度的流量也要往老版本写,你可以拿个MQ来做这个事。至于从库在数据备份完成后重新上线即可,主库的数据自然会全部同步到从库。当灰度验证通过后,我们就可以拿掉MQ然后换上新版的数据访问层和DB/Cache了。
关于灰度过程中系统链路的问题,即A/BTest实施层次的问题,就是具体在哪一层进行A/BTest,这个问题很好回答,如果是全业务那就在网关层进行,如果是具体的某个业务层那就在那个业务层进行,本质就是谁需要就在哪搞。
Spring cloud提供了灰度发布神器Nepxion Discovery,这是一个服务注册和负载均衡的增强中间件,里面包含了灰度发布的功能,大家可以去研究下。
说完了服务端的灰度发布,下面我们来聊一聊客户端的灰度发布,目前主流的客户端灰度发布分为两类:Android和iOS。其实也很好搞,Android的可以通过服务端来控制灰度,灰度期间不提交应用市场,防止应用程序自动更新,等到灰度完成后再提交应用市场。至于iOS的话,现在App Store提供了灰度功能,具体可以自行看下官网,但是有一些关键点可以提一下:
灰度过程无法控制 灰度发布机制分为7天 灰度发布遇到Bug,可以暂停发布,但是已升级用户无法回退 灰度任何阶段,都可以100%直接发布
好了,灰度发布就聊的差不多了,其实灰度发布说白就是个怎么分发请求流量的问题,解决的法办也有很多,不能生搬硬套,基于大家公司自身的情况进行即可。