干货 | 携程酒店MOCK全链路实践
作者简介
刘晓攀,携程酒店性能测试负责人,专注性能测试分析和辅助测试工具的开发。
一、前言
Mock在整个软件开发测试周期中已经非常普遍,我们也会经常有意无意地使用它。譬如开发了一段代码,这段代码强依赖了其他服务,在对方服务完成之前,肯定是期望代码能够同步开发。那么在开发的过程中一定会根据约定固定对方服务的返回,这种在代码中的模拟行为,是一种mock。
另外当前很多应用为了提高性能,普遍采用cache的方式。有些cache数据是需要很多database的数据经过一定的逻辑运算得到的,而在测试过程中,为了快速验证测试场景,直接取修改cache数据的方式,也是一种mock。
在当前五花八门的mock方式中,我们希望有一种方便快捷的mock方式,便于开发测试使用,特别是对于多依赖、多变化的场景,更需要mock的协助。否则将需要打通一系列的外部依赖来满足一个场景的需要。
于是,我们推出了mock全链路。mock全链路既是一种mock方式,又是mock在不同阶段的扩展使用。当然 mock全链路还面临着一些些问题,我们将在后面的章节中详细展开。
二、技术背景
技术的进步更多源于底层技术,比如正是由于蒸汽机的发明,才有了现在各式各样的机动车,挖土机,收割机等等,工业上各种技术也有了不同形式的发展。同理,酒店mock也是伴随着基础架构、日志系统的更新换代而不断发展。
mock全链路就是在框架能够完整跟踪应用调用链,指定header在链路上无限透传,ES日志系统大范围应用的背景下产生的 。当然不管mock怎么变化,最基础的原理还是建立在常规mock的基础上的(如图1)。
图1 常规mock示意图
首先在多依赖的应用中,特别是强依赖服务的业务逻辑比较复杂,依赖服务所在的组还有一堆任务的情况下,通过构造打通依赖服务的行为会变得非常耗时。严重的时候,会拖累整个项目的进度。
ES在公司的大规模应用,为我们解决此类问题提供了契机。ES的埋点数据为mock提供了很好的种子。框架的链路完整埋点为全链路提供最基础的管道,没有这个最基础的管道,所有的链路都将是模糊不清,无法识别的。同时 header在链路中的透传为整个mock实现铺平了道路,使得mock能够快速根据header中的规则来识别不同的链路调用。
测试的不同场景在这里也有了很好的区分。而这些数据又都可以在ES中通过埋点来解决。mock可以通过ES API把这些数据拉来使用,从而基础的API调用链路已经成形并且明晰。这就为后续的实现打下基础。
基础调用链路图如下:
Es链路埋点如图2所示:
图2 ES埋点链路示意图
当所有的链路设施都准备好之后,影响整个使用过程的就是怎么样把mock快速无缝的切入真实调用链路。
我们知道,无论是slb还是ip直连,最基本的原理还是所有的服务都需要注册,注册的最终目的就是所有的服务中心化。当调用依赖服务的时候,在中心里获取对方的服务,提供给调用方使用。
这样的情况下,应用切入mock最直接的想法就是把中心中依赖方地址修改成mock地址,当然这就需要框架提供支持。这是一种侵入式的mock应用方法。
另外一种方式是通过PROXY,类似于网络代理,所有经过代理的网络包在代理中进行收编和整理,该走mock的走mock,不走mock的放回原来的网络中。这种方式是当前相对比较理想的方式。把对应用的侵入,变成系统层面配置的更改,相对适应性更强一些。
综上两点解决后,整个mock链路的基本技术方案有了一个比较可行的落点。mock全链路的整体架构如图3。
图3 MOCK链路架构图
三、技术实现
Mock的技术实现要注意以下三点:
3.1 Mock的响应速度
响应速度要满足服务调用基本要求,不能timeout。正常情况下,mock不需要业务逻辑处理,只需要做规则的简单匹配,响应速度要快才对,这是建立在规则相对少的情况下。
如果在千万亿级的规则进来之后,规则的获取,场景的匹配速度会拖慢整个mock的响应,现在的mock系统同样要依赖缓存技术来提高规则场景的匹配速度。
另外是pb格式请求,我们要进行多次的序列化和反序列化,来保证配置的明文json或者xml能够正确的在请求中传递,而序列化和反序列化本身就是一个消耗cpu的动作,这会给响应造成极大的延迟。
当前情况下,我们尽量提前进行序列化和反序列化操作,操作结果存入缓存,这样能够在此等格式请求传递和返回传递的过程中,保持快速的字节流传递而不需要额外的序列化反序列化操作。
最后在转发的情况下,相对正常服务调用多了一次网络请求,时间消耗相对较长。在这方面我们尽量采用减少连接等待,保持连接状态等方式来减少多次socket连接造成的时间损耗。
3.2 契约依赖问题
对于依赖契约的请求格式,如pb等,需要契约的更新速度必须在ES埋点落库之前。
契约延迟更新是我们面临的很大问题,契约的不及时更新,会造成数据节点的丢失或者直接调用报错。发布订阅和编译包下载替换是我们目前解决这个问题的主要方式。
当前这需要维护一个所有应用契约文件的列表,以便我们在编译包中进行查找替换。而替换动作成功发生的前提是当前契约文件没有被进程占用。
3.3 系统操作的易用性
操作简单是用户使用的基础,也是系统的目标,当然操作习惯也会影响系统在不同人群中的使用。但最终的目标,是希望用户用尽量少的鼠标或者键盘动作,来完成整个mock的操作。链路使用操作相对简单是系统能够被使用的重要环节。
譬如ES数据拉取,我们采用了只让用户输入ES共享链接来代替繁琐复杂的ES过滤器配置。
最原始的设计UI如下图所示:需要有十几个配置项需要用户取配置:
改良后的交互如图4所示,只需要输入ES共享连接即可,其余的事情都由系统来协助用户完成,从而减少用户的使用费力度。
图4 ES数据拉取
四、面临的一些问题
在mock全链路的使用过程中,遇到一些新的问题,比如在真实调用链中重复调用的问题。由于重复调用的返回结果不一致,导致我们在mock全链路的时候无法知晓哪个是第一次要返回,哪个是第二次要返回,从而影响全链路的真实性。
缓存问题。之前提到过很多应用都会在本地或者其他缓存服务器做数据缓存,很多处理逻辑是缓存数据存在的情况下,拉取缓存数据,否则走依赖接口调用,在mock切入应用之后,发现很多缓存未过期导致mock无法被调用,从而影响全链路的正确性。
转发302问题。有些服务需要在服务API之间做302转发调用,而mock正常是要返回http code 200的状态的。
针对不同业务的问题,我们只能针对性的进行个性化或者通用兼容性修补,在通用系统的基础上,进行个性业务适应。
总之,mock全链路系统需要在实际业务中进行不断的锻造、更新,在使用中不断调整,从而保证mock全链路能够适应不同业务的需要。
五、后记
Mock的设计特别要考虑压测的需求,压测对mock系统的要求会更高,所以我们在设计之初,要把这种需求优先考虑,对系统扩展和快速资源扩容留下足够的空间。
另外匹配规则的设计要尽量简单,譬如正则和xpath, jpath此类的功能,从当前的统计数据来看,使用率不高;而简单的规则,譬如header匹配,包含等则被用户大面积使用。
大数据与人工智能沙龙
8月24日上海
火热报名中
↓↓↓
【推荐阅读】