导读:百度测试中间件是百度QA自主研发的底层基础技术,历经10年的不断发展,采用数据平面+控制平面的总体架构,与google istio设计理念异曲同工,支持8大功能,9大测试场景,覆盖百度集团各大产品线,目前接管拓扑1w+,接管链路1w+,利用中间件技术,可以大幅提升联调环境搭建效率、模块免测率,大幅提升环境仿真度,线上环境安全性,是测试中必不可少的核心工具。
引言
本文提出一种基于中间件技术的测试方案,并介绍该方法在百度各类测试场景中的应用实践。
一、百度测试中间件是什么
百度测试中间件(以下简称中间件)是百度QA自主研发的中间件,历经10年的技术发展,目前已经成为百度各条产品线测试中必不可少的基础设施。它是一种网络代理,接管模块之间的通讯链路后,在数据层面可以解析、修改数据包,在行为控制方面,可以控制请求和返回数据的路由行为,可以加入熔断、过滤、限速等机制,从而满足各类测试场景的需求。
纵观业界,各类网络代理多种多样,其中技术影响力最大的是google的istio,简单来讲,istio系统由两部分组成:
1、数据平面
数据平面指的是网络代理这层,底层采用envoy作为代理,以sidecar的形式与用户服务同机部署,服务与服务之间不再直接通讯,而是先和本机的envoy先通讯,envoy与envoy进行通讯,envoy可以对通讯数据做数据层面或者行为方面的控制。
2、控制平面
控制平面就像是大脑,可以给数据平面下发控制策略,数据平面充当执行者的角色,执行控制平面下发的控制策略,同时控制平面还可以收集数据平面的状态,从而了解整个系统的状态。
可以看到,istio采用数据平台+控制平面的设计思想,可以有机的控制整个系统,那么是否可以将此设计思想应用到测试领域呢?答案是肯定的,百度内部在2010年左右就有类似的网络代理,在2016年给网络代理加上了控制平面,2017年在公司内发布,在各条产品线不断应用落地,整体思想与istio异曲同工,发布时间也是不分前后。不同的是,istio更多的是从开发或者运维角度来管理整个系统,而中间件更加侧重于测试场景所需的各种能力,例如录制、回放、路由、改包等能力,并补充测试场景所必须的组件,提供整套开放式API,方便与各类环境搭建工具或测试驱动程序相结合,从而满足各类测试场景。
二、测试环境的挑战
让我们把目光转移到日常的测试工作中,在测试的各个阶段都需要搭建测试环境,尤其是联调测试,更需要快速搭建一套高度仿真的测试系统。这里以被测版本B1需要联调测试为例,从历史经验来看,我们大概经历了以下三个阶段来满足此类功能联调需求:
- 完全搭建一套联调环境:整个拓扑需要搭建7个模块,如果每个模块联调都需要搭建这样一整套环境,很浪费机器资源,并且环境准备时间很长,显然不是一种好的解决方案。
- 维护一套基准版本的全系统:首先搭建一套完整的基线版本的测试系统,然后将测试版本的B1部署至基线环境,这种方案会导致整个系统的稳定性会变差,在多个项目并发的情况下,稳定性问题会成倍扩大,仿真度也不满足需求。
- 维护一套基准版本的全系统,进行分时复用:与上面方案二大致相同,不同的是,不同模块在使用环境时需要排队进行分时复用,稳定性虽然有所提升,但测试效率非常低。
除了环境仿真度和测试效率方面带来的挑战,随着越来越多的模块微服务化,模块数量暴增,服务间依赖的等级也不尽相同,如何组建一套测试环境来测试系统的整体稳定性又是另外一个层面的挑战。
我们可以把上面的问题进行进一步的抽象,在联调测试中,我们面临的是测试资源和测试效率方面的问题,我们是否可以复用一套测试资源,将测试流量进行隔离呢?这里需要的就是中间件的路由能力,在测试系统整体稳定性时,我们需要一种灵活的模拟机制,在通讯链路层面模拟各类网络异常,这里需要的就是中间件的链路控制能力,所以我们把各类测试场景抽象后,都可以归结为某种链路数据或链路行为层面的问题,而这正是中间件所擅长的,在下面的章节中会具体介绍中间件系统的设计思路。
三、设计思路
1、系统组成
- 申请平台:整个系统是以服务化的形式对外提供服务,以平台或者API的形式与用户或其他系统进行交互,申请平台的作用就是将用户的需求转化为配置中心可以识别的配置。
- 配置中心:即控制平面,是整个中间件系统的大脑,接收申请平台发过来的配置,以结构化的配置进行存储。配置中心以zookeeper为底层,对整个被测系统进行了3个层次的抽象:
1)链路信息:对每个产品线的不同链路的通讯协议进行描述,包括请求和返回方向的包头、包体格式等信息,通过这些配置,中间件代理可以解析通讯链路中的数据,为进一步在数据和行为方面的控制打下基础。
2)拓扑信息:环境搭建工具会将整个拓扑中各个模块的部署信息通过API传递给中间件系统,这样中间件系统就可以知道各个模块的部署地址,从而完成通讯数据的整个转发过程。
3)策略信息:策略信息就是控制平面希望数据平面需要做的具体事情,此项配置来源于申请平台,例如用户可以在申请平台选择需要控制的链路,并指定该链路的行为,例如路由、录制、改包等,申请平台会自动将用户需求转化为中间件代理可以识别的配置信息,中间件代理对此配置实时热加载并即时生效。
除此之外,中间件代理还会将所代理链路的地址(中间件链路地址)自动汇报给配置中心的NB_CLUSTER节点,上游模块连接到此地址,完成对中间件代理的服务发现过程。
- 中间件代理:是整个系统的执行者,可以实时watch配置中心的配置变化,热加载实时生效,采用c++语言编写,性能非常高,中间件代理通过接管系统中的部分或全部链路,与测试环境组成满足特定需求的测试环境。
- 数据中心:由于测试场景的需求,中间件系统接管通讯链路之后,需要录制请求和返回方向的数据,后面可以利用中间件代理+录制数据完成对真实后端模块的模拟,即mock后端模块,中间件所录制的数据就存储在数据中心中。
- 日志中心:由于中间件代理可以是个远程的集群,集群中的有多个同质化的实例,也可以以sidecar的形式部署在被测模块所在的机器上,如果日志只在本地存储,后续定位问题时将会非常的繁琐,所以需要一个集中式的日志中心将所有日志进行存储,并提供日志查询服务。
- 控制中心:控制中心与配置中心的作用类似,都是向中间件代理下发控制命令,不同的是,配置中心是面向全局的控制,即所有的中间件代理都可以从配置中心加载相同的配置,并且这类控制策略都是由用户提交的测试需求,而控制中心可以单独给某个中间件代理发送控制命令,是一种局部控制,这类控制策略多数是由中间件开发者或维护者发出,多数用于运维或管理需求,例如在定位问题时,需要单独打开某个代理的debug日志,可以通过控制中心进行单独控制。
2、工作模式
整体来讲,中间件代理分为两大工作模式,即集群模式和本地模式,两种模式各有所长,能够满足不同的测试场景。
- 集群模式:中间件代理以远程集群的方式提供服务,集群中部署多个同质化的实例,在这种模式下,中间件代理可以动态开启和释放链路,用户的每个申请对应于一条链路的开启,每个申请的释放对应于一条链路的释放,整个中间件集群支持2万条链路同时工作,可以理解为整个中间件集群是一个非常强大的代理中心,由于被测模块与中间件集群是远程通讯,所以不可避免的会带来通讯延时方面的开销,所以此模式适用于对通讯时延不敏感的测试场景,例如diff、stable测试、功能测试或功能联调测试等。
- 本地模式:顾名思义,是将中间件代理与被测模块以sidecar的形式进行同机部署,由于是同机部署,被测模块和中间件代理之间几乎没有通讯延迟,此模式适用于对通讯延时敏感的测试场景,例如性能测试场景,对后端的性能波动要求非常高,还有混沌场景,由于本身就是要在通讯过程中注入延时,如果使用集群模式还会带来额外的通讯延时,造成试验效果不准确,除此之外,由于混沌场景大多采用线上真实环境进行试验,整体流量非常大,如果采用集群模式,会需要非常多的资源才能搭建出来并满足测试需求。
3、整体工作流程
- Step1申请服务:用户通过申请平台或者API描绘测试需求,本质上来讲,就是告诉中间件系统希望在哪条链路执行什么样的操作,例如在A2B链路执行路由操作,路由到B1
- Step2生成链路:申请平台将用户需求转换为策略配置,存储在配置中心中,中间件代理通过watch机制,实时加载策略需求,并实时生成对应的中间件链路,例如图中的A2B和B2C链路。
- Step3链路接管:中间件链路生成后,会将中间件链路地址汇报给配置中心,环境搭建工具在搭建上游模块时,会通过中间件的服务发现API得到中间件A2B、B2C链路的地址,并将A、B模块的下游连接到中间件,此外环境搭建工具还会将整个拓扑的部署信息以API的形式传递给中间件,所以中间件各个链路可以完成对下游模块的服务发现,即A2B链路的中间件连接到模块B,B2C链路的中间件连接到模块C,这样中间件就完成了对A2B和B2C链路的接管。
- Step4策略生效:用户通过给整个环境的入口模块A发送请求后,此请求会流经中间件的各条链路,中间件会判断当前链路是不是用户指定的链路,如果不是,则直接转发给拓扑节点中配置的下游,如果是,则执行用户指定的策略需求,例如A2B链路,中间件就会将请求转发给B1,B1的下游连接B2C链路的中间件地址,这样这个请求就绕过了基线版本的B,整个请求的转发路径为AàB1àC,此外用户还可以在其他链路指定不同的测试需求,例如录制需求,中间件就会在指定链路将请求和返回的数据保存到数据中心。集群模式与本地模式的中间件工作机制是类似的,不同的是,集群模式的中间件是面向所有用户的,每个用户的需求对应其中的一条链路,本地模式的中间件只会服务于某个特定用户,所以稳定性和转发效率更高。
四、应用场景
中间件在百度内部的测试场景应用非常广泛,从线下测试到线上测试场景都有所应用,例如在功能测试阶段,利用中间件的改包能力,可以模拟各种返回数据,通过联调平台,用户可以在一套基线环境内,利用中间件的路由机制,组成多路复用联调环境,满足多个测试项目同时联调,在线上环境中,用户通过中间件精准的网络异常注入,可以实现请求级别的混沌场景,下面会一一进行具体讲解。
1、功能测试
功能测试在APP端测试中应用非常广泛,中间件会生成一个链路,接管端和服务端之间的链路,具体工作流程如下:
- 创建空链路:中间件为每个产品线创建一条没有任何策略的数据通路,即mock代理
- 创建mock策略:在mock代理中创建mock策略,包括端识别策略,服务端识别测试和改包策略,中间件根据端识别策略和服务端识别策略动态识别请求并执行对应的改包策略,同一产品线的多个用户可以共用一个统一的mock代理从业务需求角度来讲,在测试端功能的时候,对服务端的返回数据需要进行稳定的数据MOCK和异常的数据MOCK。
- 稳定的数据MOCK:这种模式是为了满足端上特定的功能场景,例如模拟固定的模板数据,回归不同模板数据在端上的效果。
- 异常数据MOCK:这种模式是是为了测试端上加载异常数据时的表现,例如测试APP端在加载异常数据的情况下是否crash。
从中间件的角度,改包策略有四种,AUTO,DATAID,CALL_BACK和USER_DEFINE模式,分别满足不同的测试场景。
- AUTO:自动修改模式,中间件内置了29种自动修改数据策略,例如根据不同数据类型自动模拟边界值,跨类型修改等。
- DATAID:数据替换模式,可以对返回数据进行全部或部分替换。
- CALL_BACK:允许用户以API的方式提供数据,中间件通过调用API获取用户数据,然后进行MOCK。
- USER_DEFINE:这种方式允许用户对服务端返回的真实数据实时进行修改,用户可以指定数据中的某个key,并修改为用户指定的value,用于满足特定的测试场景。
2、系统级测试
系统级测试场景指的是单模块的diff、stable测试场景,此场景需要保证基线版本和测试版本的模块具备相同的输入和后端返回,测试被测模块的返回是否有diff,具体工作流程如下:
- 接管链路:搭建录制子环境,中间件接管录制链路
- 发压录制:压力云给录制子环境发压,即可进行录制
- 保存数据:录制结束后,A2B数据保存至请求数据管理平台,C2B,D2B,E2B数据保存至数据中心
- 后端mock:搭建回放子系统,被测模块B和B’,分别连接中间件回放链路,中间件从数据中心中读取数据并返回,完成后端mock
这里要特别说明的就是数据中心存储数据的方式,数据中心底层采用的是redis,是一种典型的key-value存储,所以对数据key的设计尤为重要,这里采用的是任务标识+链路标识+请求标识三种因子联合计算sign,形成key,任务标识可以区分不同申请任务的数据不相互覆盖,链路标识可以区分同一个请求不同链路的数据不相互覆盖,请求标识可以将同一个任务,同一个链路中的不同请求进行区分。value是返回方向的数据,这样在数据录制阶段就形成了请求和返回数据之间的对应关系,所以在回放子环境中,同样的请求发来的时候,中间件通过计算会得到相同的key,然后得到对应的value,并将此数据返回给上游模块,这样就对真实后端进行了mock,从而被测模块B’的后端都会有稳定的一致性返回,能够更精准的测试出基线版本的B模块和测试版本的B’模块自身的diff。
3、联调测试
在文章开头,我们提到了联调测试面对的痛点问题,在这里我们利用中间件的请求级路由能力,复用一套基准环境,具体工作流程如下:
- 链路接管:维护一套基准环境,中间件接管各条链路
- 染色标记:发压工具对请求进行标记
- 路由:中间件根据请求标记,在指定链路路由到指定下游
- 被测模块与基准环境组成了一套完整的联调环境
这里要特别说明的是,这种联调模式是对原有联调环境的巨大颠覆,将多路复用思想体现的淋漓尽致,对测试效率的提升是巨大的,对测试资源的节省也是巨大的,用户还可以对此环境进行极致的多路复用,即不单单是在不同链路进行路由功能,还可以在不同链路进行数据录制或者改包,这样就可以把这套环境打造为一套真正的多路复用系统。
4、沙盒测试
沙盒系统是在线下搭建的一套仿真测试系统,在流量仿真、环境仿真和环境搭建效率方面都有很高的要求。
- 流量仿真:传统方式是采用线上引流方式,将线上流量实时复制到线下沙盒环境,但这是一种“靠天吃饭”的方式,流量仿真的偶然性非常大,在测试系统中,往往需要补充特殊的测试流量,以满足特定的测试场景,所以此时可以引入中间件,抽取一定比例的线上流量,并实时修改为特定请求的样式转发给沙盒系统。
- 环境仿真&环境搭建效率:沙盒系统虽然是一套仿真系统,但在资源有限的情况下,只能在一定程度内进行环境仿真,在前端模块测试时,希望后端环境是仿真的,所以可以利用中间件的动态路由机制+安全策略访问线上后端环境,在后端模块测试时,同样可以利用路由机制,复用沙盒环境中的前端模块,由于被测模块并没有真正部署到沙盒基线环境中,所以并不会对整个沙盒系统带来稳定性方面的问题,在多个项目同时进行测试时,环境的复用度会进一步提升,从而可以进一步提升环境搭建效率。
上面提到的中间件安全策略有3种,即熔断、过滤和限速策略。这三种策略是基于线上问题的抽象。
- 熔断:中间件在转发线下请求的时候,首先会调用线上后端稳定性API,当线上环境发生异常时,中间件会即时熔断,当线上环境恢复时,中间件会自动恢复对线上环境的访问。
- 过滤:线下测试请求可能会对线上环境造成破坏,例如线下环境不小心打开了降级开关,降级流量如果直接发给线上环境,会直接造成线上环境降级,中间件会对降级和低质流量进行识别并直接过滤。
- 限速:线上环境分配给线下测试的容量是固定的,所以中间件需要做整体限速,可以做到对数据包的限速或者对某种请求标识进行限速,从而对线上环境进行保护。
5、混沌工程
混沌工程是中间件以sidecar的形式部署到被测模块所在机器,并支持部署在client端或server端,例如如果需要模拟第三方依赖的异常,由于是第三方依赖,中间件往往不方便部署在第三方依赖的服务上,所以可以部署在client端,如果是大扇出模块,每条扇出链路都会有很多的实例,在混沌测试时需要充分模拟被测模块对后端的调度机制,所以可以将中间件部署在server端,在调度仿真度上做到100%仿真。中间件可以支持对交互数据的异常和交互行为的异常,并能够深入业务场景,精细化的模拟网络交互异常,例如模拟模块建立连接之后的断开,这是其他工具无法做到的,配合染色机制,中间件可以做到精准异常注入,做到query级别的异常注入。
工作流程如下:
- 染色标记:将真实流量进行染色
- 异常注入:中间件只对染色流量进行异常注入,非染色流量直接转发下游
- 中间件对染色流量进行请求级精确异常控制,精确控制爆炸半径,正常流量不受损失
五、技术生态
中间件系统通过多年的落地打磨,目前已经服务于百度内部各大产品线,落地9大测试场景,通过对9大测试场景的抽象,将不同测试场景抽象为8大原子基础测试能力,通过不同原子基础能力的不同组合,满足不同的测试场景,一方面,中间件系统采用同一套基础架构+8种基础能力不断辅助业务发展,另一方面,通过不断的业务落地反哺技术的提升,形成双向有益的技术生态。
六、技术本质
中间件从最开始的简单的网络代理不断发展,结合控制面强大的控制能力,通过对通讯数据和通讯行为方面的控制,逐步过渡到对整个系统的控制,中间件已经是测试环境的一部分,与被测模块共同组成用户所需的特定用途的测试环境。
百度架构师
百度官方技术公众号上线啦!
技术干货 · 行业资讯 · 线上沙龙 · 行业大会
招聘信息 · 内推信息 · 技术书籍 · 百度周边
欢迎各位同学关注!