写点什么

携程酒店 MOCK 全链路实践

2020 年 2 月 19 日

携程酒店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 匹配,包含等则被用户大面积使用。


作者介绍


刘晓攀,携程酒店性能测试负责人,专注性能测试分析和辅助测试工具的开发。


本文转载自公众号携程技术(ID:ctriptech)。


原文链接


https://mp.weixin.qq.com/s/PXlE3UPBY0-bMyrltbXxaA


2020 年 2 月 19 日 20:30251

评论

发布
暂无评论
发现更多内容

这些年看过的Linux相关书籍推荐

我是程序员小贱

目前数字人民币试点仍是“4+1” 别误读了

CECBC区块链专委会

数字货币 央行 人民币

全面了解CGI、FastCGI、PHP-FPM

书旅

CGI PHP-FPM Fast-CGI

正则表达式位置匹配——匹配两个特殊符号中间的内容

jerry.mei

Java 正则表达式 前端 字符串匹配

16张图入门Nginx——(前端够用,运维入门)

执鸢者

nginx 运维 前端

翻译: Effective Go (7)

申屠鹏会

golang 翻译

MySQL 基准测试

多选参数

MySQL

Docker搭建PHP+Nginx+MySQL+Redis

书旅

Docker 镜像 lnmp

这样看mybatis,谁都会分析源码!

诸葛小猿

源码 mybatis mybatis源码

Spring如何选择类构造器

申屠鹏会

golang 翻译

学习技术先从学会使用搜索引擎开始

我是程序员小贱

华为的“少年天才”攀登者,出发向智能存储的“奥林帕斯山”

脑极体

解析 HashMap 源码概括

shengjk1

Java hashmap

为什么考研,考研能给你带来什么?说说我的感受!

我是程序员小贱

平均负载是什么?

我是程序员小贱

敏捷到底是个什么鬼?

刘华Kenneth

程序员 敏捷 change

一次由默认参数引起的思考

Lart

编程 思考

JDK中居然也有反模式接口常量

看山

Java 源码阅读

螺旋矩阵算法,臭代码解析,微服务架构 Service Mesh 服务网格 RPC 协议实现原理 Dubbo 通讯协议,John 易筋 ARTS 打卡 Week 13

John(易筋)

ARTS 打卡计划

解析 hashMap 源码之基本操作 get

shengjk1

Java hashmap

毕玄大佬的分享以及给我的感悟

白色蜗牛

Java 程序员 技术 职场 架构师

航运区块链 抗疫危中有机

CECBC区块链专委会

区块链 航运

1 学习性能优化的要点

我是程序员小贱

区块链技术--公证人机制

CECBC区块链专委会

区块链 数字货币 公证人

让你起飞的20个Linux命令骚操作

我是程序员小贱

解析 HashMap 源码之基本操作 put

shengjk1

Java hashmap

Bash 脚本的单元测试

柴锋

bash Linux DevOps Unit Test Shell

结算场景下的跳坑记

墨凡

1 时间复杂度总结

我是程序员小贱

高效程序员的45个习惯:敏捷开发修炼之道(1)

石云升

读书笔记 敏捷开发

Rust特征与泛型区别点

编号94530

rust 泛型 封装、继承、多态

携程酒店MOCK全链路实践-InfoQ