内容简介:假如我们有一组后端服务为用户提供某些功能,各服务之间的调用关系(简化的)可能如下图在实际的工作中,我们经常遇到这种场景:同时有多个feature在开发,比如feature_x,feature_y,feature_z,这些feature的代码改动可能涉及到若干个相同或者不同的的后端服务,例如:
假如我们有一组后端服务为用户提供某些功能,各服务之间的调用关系(简化的)可能如下图
在实际的工作中,我们经常遇到这种场景:同时有多个feature在开发,比如feature_x,feature_y,feature_z,
这些feature的代码改动可能涉及到若干个相同或者不同的的后端服务,例如:
1)feature_x修改了app_srv0的<用户账户展示> 接口(假设该接口的名字是user_account_show),user_account_show接口依赖app_srv1的 <账户余额接口>user_account_balance
2)feature_y特性修改了user_account_balance接口,这个修改承诺接口的对外输出不改变,只是修改了用户的账户余额计算公式。
在feature_y测试通过之前”user_account_balance接口对外输出不改变“这个承诺是无法得到保障的,如果我们只有一个测试环境,多个feature都只能在这个测试环境上进行测试,那么feature_x,feature_y的测试只能串行。如果并行进行测试的话,服务部署会是下面这个样子的(浅绿表示feature_x,紫色表示feature_y):
测试过程中如果feature_y有bug导致user_account_balance返回的数据不对,就会间接导致user_account_show接口返回的展示信息也不对,从而导致测试认为。但实际feature_x的修改。
常用的解决办法
1)测试串行,缺点:时间周期常,项目feature上线慢
2)并行部署多套测试环境,缺点:资源成本和维护成本都很高,如果服务众多或者并行开发的版本多那么基本不具备可执行性
3)使用一套环境强行并行测试,缺点:特性之间的相互影响可能导致多个特性的测试都需要返工,拖长各个feature测试的耗时
解决方案设想
先不废话,上个图
由上面的分析我们可以知道要考虑的是“怎么已较小的代价实现测试环境特性隔离”,“代价小”也就是尽可能少的部署服务,只部署有修改的服务即可;“特性隔离”就是不同特性分支的请求分别路由到支持对应特性的服务版本上。参考上图:
feature_x修改了app_srv1,app_srv4,app_srv6,分别得到新版本服务app_srv1_x,app_srv4_x,app_srv6_x
feature_y修改了app_srv4,app_srv7,分别得到新版本服务app_srv4_y,app_srv7_y
假设feature_x版本的请求为Rx,feature_y版本的请求为Ry,基线版本的请求为R0,只要做到Rx,Ry请求分别走不同的调用链即可保证feature_x和feature_y的测试相互不影响,也就是:
Rx的调用链:app_srv0 —> app_srv1_x —> app_srv2 —> app_srv4_x —> app_srv6_x —> app_srv7
Ry的调用链:app_srv0 —> app_srv1 —> app_srv2 —> app_srv4_y —> app_srv6 —> app_srv7_y
如何做到这一点
让请求携带路由信息(以下格式为非标准json格式,只是类似格式方便展示而已)
在请求的公共头部携带一个版本绑定的路由信息,比如编号feature_x_20180101,feature_y_20180101
//feature_x //comm_head { "route_id":"feature_x_20180101" } //feature_y //comm_head { "route_id:feature_y_20180101" } 复制代码
存储一张路由表
//route_table { "feature_x_20180101": [ { "appid": "app_srv1", "srv_version": "app_srv1_xxx", "addr": "ip:port" }, { "appid": "app_srv4", "srv_version": "app_srv4_xxx", "addr": "ip:port" }, { "appid": "app_srv1", "srv_version": "app_srv6_xxx", "addr": "ip:port" } ], "feature_y_20180101": [ { "appid": "app_srv4", "srv_version": "app_srv4_yyy", "addr": "ip:port" }, { "appid": "app_srv7", "srv_version": "app_srv7_yyy", "addr": "ip:port" } ] } 复制代码
每个服务维护自己的appid,发起rpc的服务通过某种方式获取路由表配置,从获取的路由表中寻找映射的appid,和被调用目标appid(调用方一定知道被掉方的appid)进行对比,找到需要调用的服务地址和端口,例如:
feature_x的请求Rx到达app_srv0之后,app_srv0需要调用app_srv1,app_srv0解析出请求头部(comm_head)的route_id:feature_x_20180101,使用route_id获取到路由表feature_x_20180101项下面的所有配置,从获得的配置中寻找appid=app_srv1的项(这里服务的数量是很有限的,不必担心遍历的性能),获取对应的ip和端口发起调用,这样就可以是Rx请求触发的第一次rpc调用的是feature_x版本的app_srv1_x而不是基线版本的app_srv1或者feature_y版本的app_srv1_y,如果找不到对应的想就采用默认路由调用,也就是调用基线版本;发起调用的时候透传请求头里面的route_id,这样后续调用链的每个服务都能使用上述过程找到请求要调用的版本分支,这里需要增加的就是对路由表的维护工作以及客户端发起请求的时候需要增加请求头的写入。发布上线之后也可以采用这种方法进行AB test,如果不需要可以对配置进行修改或者前端发起请求的时候不携带路由信息。
以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持 码农网
猜你喜欢:- 推荐一款Python开发环境管理神器
- Vagrant 2.2.5 发布,虚拟化开发环境管理
- 管理Mac的Python环境
- Python多环境扩展管理-Virtualenv
- 授权管理-LDAP-介绍与环境搭建
- 如何管理多个Python版本和虚拟环境
本站部分资源来源于网络,本站转载出于传递更多信息之目的,版权归原作者或者来源机构所有,如转载稿涉及版权问题,请联系我们。