最后说一下灰度发布组件,分为了分析,设计,实现三个部分,首先来说分析
需求的场景
我们说的接口限流和幂等框架的项目背景,因为此中有一个公共服务平台,提供公共业务功能,给其他的产品后端系统调用,避免重复开发相同的业务代码
公共服务平台提供的是,在某个开源PRC框架的RPC格式的接口,在上线一段实践课,发现RPC的框架不稳定,导致接口会出现bug,于是准备替换这个RPC框架
对于引入新的框架,我们要求是成熟,简单,和Spring相吻合,于是最后直接使用Spring框架提供RESTful格式的远程接口
那么,我们需要修改本端的代码外,还需要修改对端的接口,那么我们希望,可以最高效的那个接口暴露方式,对业务代码基本没有改动,我们也不保证完全不出问题,所以最好能灰度替换掉老的RPC服务
怎么去灰度替换
因为替换的过程是灰度的,老的RPC服务不能下线,还要部署另一套全新的RESTful接口,然后验证过后,就逐步替换,称为新的RESTful接口
万一调用过程中,出现了问题,我们需要将调用方的代码回滚,重新部署,导致后来新开发的业务也会被回滚,为了避免这种情况发生,还得进行二次开发
于是,为了不影响调用方本身的开发进度,就思考如何替换
在替换过程中,调用方并不删除老的RPC接口代码逻辑,而是新增调用RESTful的接口代码,然后通过一个控制开关,来灵活控制走老的还是新的代码逻辑
boolean callRestfulApi = true;
if (!callRestfulApi) { // 老的调用RPC接口的代码逻辑 } else { // 新的调用Resful接口的代码逻辑 } |
如果为true,走老的逻辑,false,走新的RPC接口
不过,更改了callRestfulApi的值需要修改代码,而修改代码就需要重新部署,这样的设计不够灵活,优化的话,可以将这个值放在配置中心或者配置文件中
为了更加保险,我们希望这个控制的粒度越细越好,就是简单的几个接口,然后慢慢的增加调用的接口比例,最后,将所有的接口请求,替换为新的接口,这既是灰度替换
如何实现呢,对于一个业务系统,灰度的对象,针对请求携带的时间戳信息,业务ID等信息,按照区间,比例或者具体的值来做灰度
比如使用用户ID,我们先将ID尾号为 918 879 111的查询请求调用新接口,验证无误后,扩大范围,在变为 1000-5000的用户ID调用新接口
最后依次的扩大范围,知道灰度达到100%,这样运行还是无误后,调用方将老的代码逻辑删除了
类似的灰度需求场景还有很多,金融产品中,我们将贷款ID作为灰度对象,对应部分贷款应用新的算法,如果没有问题,继续扩大区间范围
除此外,为了保证代码万无一失,我们最好做一些功能开关,方便老代码和新代码的切换
需求分析
从实现的角度来讲,调用方只需要将灰度规则和功能开关,按照某些实现约定好的格式,存储到配置中心或者配置文件,在系统启动的时候,从中读取到配置到内存,然后根据灰度配置的范围来判定是执行新的代码逻辑,还是走老的代码逻辑
然后根据能抽象就抽象的原则,抽象出一个灰度的代码,提供给各个调用方来复用
这里的灰度都是些代码级别的灰度,而一般常见的灰度,是产品或者系统层面的灰度
常见的产品层面的灰度,类似A/B Testing,不用的用户看到不同的功能呢,对比用户的体验,收集数据,改进产品,所谓的系统层面的灰度,往往不在代码层面上实现,通过配置负载或者API-Gateway,来实现分配流量到不同的系统上
如何实现
我们从实现的角度,组件的使用者需要设置一个key值,来唯一标识要灰度的功能,选择一个灰度对象,在配置文件或者配置中心中,配置这个key对应的灰度规则和功能开关
配置文件格式如下:
dark:
–key: call_newapi_getUserById enabled: true // enabled为true时,rule才生效 rule: {893,342,1020-1120,%30} // 按照用户ID来做灰度 –key: call_newapi_registerUser enabled: true rule: {1391198723, %10} //按照手机号来做灰度 –key: newalgo_loan enabled: true rule: {0-1000} //按照贷款(loan)的金额来做灰度 |
灰度组件在业务系统启动的时候,会将这个灰度配置,按照事先定义的语法,解析并加载到内存对象中,业务系统根据组件的灰度判定接口,取到设置好的值,判断是否执行新的代码逻辑
总结一下,灰度组件和限流框架类似,主要包含的功能由两部分功能,灰度规则配置解析和提供编程接口DarkFeature判断是否灰度
本章总结:
灰度发布分为三个不同的层面的灰度,产品层面的恢复,系统层面的灰度和代码层面的灰度,我们重点讲解代码层面的恢复
代码层面的恢复,主要为了解决代码质量问题,为了逐渐的替换新的代码,并且在新的代码出现了问题后,不需要大量的回滚代码,相比较于系统层面的灰度,更加的细粒度
但是存在的灰度代码和业务代码耦合的问题’
课后思考
对于非功能性的需求,进行一下分析
本质上,这是一个将原本的老接口和新接口利用表示来隔离开来的功能,这就是一个防腐层
莫名想到了Enum和Iterator的关系
对于这个组件的实现,可以采用切面编程或者提前的过滤器创建
关于这个组件的非功能性需求
1.要求延迟低,不能引用过于复杂的算法去计算如何区分
2.要求异常不影响,如果出现了取值错误或者计算错误,不能影响业务一同的正常调用
3.耦合度低,尽可能的低侵入,这一点可以利用切面或者过滤器实现
4.最好提供对应接口,能让用户实现如何获取到要拦截的值,配置文件书写的值如何去取