一. 前言
你是否和我一样遇到过以下的问题?
1)服务重构,一堆接口需要回归,让人头疼
2)每次迭代,都要花很多精力来进行回归测试
3)线上bug,线下复现不了
4)接口自动化用例写辛苦,维护更辛苦
… …
或者许你正在被这些问题困扰。你可能和我一样也尝试过一些流量回放工具来解决上述问题,但最终经历了从入门到放弃的无奈。现有大部分流量回放工具中都存在这样那样的限制,比如只支持GET接口、不能对子调用进行mock、对环境和数据依赖高等,所以往往线上录制时心惊胆战,线下回放时坎坷不断。回归的接口少,使用流量回放还不如手动测试快。回归的接口多,使用的坎坷让人绝望。
所以我们需要的是一款简单易用、安全可靠的流量录制回放工具!!!
在此,推荐jvm-sandbox-repeater。
二. jvm-sandbox-repeater简介
jvm-sandbox-repeater是阿里在19年7月份的时候开源的流量录制回放工具,代码提供了录制回放的能力,以及一个简单的repeater-console的demo示例。github 地址:https://github.com/alibaba/jvm-sandbox-repeater。
jvm-sandbox-repeater框架基于JVM-Sandbox,具备了JVM-Sandbox的所有特点封装了以下能力:
1.录制/回放基础协议,可快速配置/编码实现一类中间件的录制/回放
2.开放数据上报,对于录制结果可上报到自己的服务端,进行监控、回归、问题排查等上层平台搭建
基于它,我们可以在业务系统无感知的情况下,快速扩展 api ,实现自己的插件,对流量进行录制,入口请求(HTTP/Dubbo/Java)流量回放、子调用(Java/Dubbo)返回值Mock能力。详细介绍可以看官方说明。
录制回放主要原理如下:
录制:如图,当repeater启动对service A的录制后,有请求到service A,sandbox感知到请求后通知repeater。repeater对事件进行过滤和采样计算,对满足录制条件的请求会记录请求、响应、子调用和响应,序列化成后通知repeater-console进行处理和保存。
回放:回放时,用户请求repeater-console的回放接口,明确需要回放哪条录制数据。然后repeater-console通过调用repeater提供的回放任务接收接口下发回放任务。repeater在执行回放任务的过程中,会反序列化记录的wrapperRecord,根据信息构造相同的请求,对被挂载的任务进行请求,并跟踪回放请求的处理流程,以便记录回放结果以及执行mock动作。如图,当我们启用了redis插件,录制时,service A到reids等的子请求方法、参数、响应将被录制下来,回放时,当service A再对reids发起请求时,repeater会先判断是否需要mock,当需要mock时会根据回放上下文中的信息拼接出MockRequest,通过mock策略计算获取MockResponse。目前源码中是获取相似度100%的请求的响应来进行mock。回放结束,repeater会将回放信息和结果序列化后通知repeater-console进行处理和保存。
进一步了解可以阅读源代码或者参考大能文档:
录制流程:https://testerhome.com/topics/20962
回放流程:https://testerhome.com/topics/21046
三. 我们的落地实践
jvm-sandbox-repeater 仅仅提供了录制回放的能力,如果真的需要实现业务回归使用,后面须要有一个数据中心负责采集数据的加工、存储、搜索。repeater-console提供了简单的demo示例,它支持本地或者mysql存储和获取录制回访结果,可进行单请求的录制回放,使用可参考官方文档。而真实的使用场景中,我们一般需要的是批量的录制、回放以及结果查看,所以需要写一个自己的repeater-console,另外也还需要实现更多的repeater-plugins。官方已经有了一些常用的插件,所以我们根据需要,从没有的入手。主要改动有:
1)根据需要,先实现了SOA、mongo和es插件,后续还需要慢慢加。框架封装了基础录制回放协议,对于普通插件开发可以快速完成,主要成本在于寻找最合适的插桩埋点
2)将录制、回放都结果存入es,并增加了一些字段方便数据搜索查询。
3)在RecordFacadeApi.java中增加了批量获取录制结果、批量回放、批量获取回放结果接口。
4)console独立部署,配置获取接口/facade/api/config/%s/%s从数据库获取配置,大大减少了对目标服务器资源的占用。
5)repeat接口支持diff,diff结果也存入es。回放结果查询时,支持diff过滤。
四.结果展示
完成上述改造,基本上流量回放就可以简单使用起来了。下面记录一次录制回访的过程。
1)在目标服务器上运行./sandbox.sh -p {PID} -P 12580启动录制,看日志见插件加载成功,服务开始录制
2)批量查看录制结果
3)批量回放。
一般我们录制和回放的服务器不是同一台,且console独立部署了,所以这里需要指定期望回放到哪一台服务器。
另外为了满足只回放某个接口的请求,我们对batchGethe和batchRepeat接口也增加了对指定接口的支持。
4)批量回放结果查看
在回放结果入库时,我们对originResponse和response进行了diff,和回放结果一起存入es。这样就可以指定只看回放成功或者失败的用例。下面这条是回放成功的,可以看到diff为false,diffReseult为null。
增加了请求接口信息requestApi,方便知道是哪个接口的回放。response是回放求结果,originResponse是录制的原始响应,可以看出来结果一致。mockInvocationEsLists是mock的插件方法和参数。
当想获得比对失败回放时,参数diff传0,结果如下
5)资源占用情况
在服务器上的录制。启动repeater开始录制,约占用80M的内存。另外短时间也会有较大的cpu开销,因为需要遍历所有加载的类以及类增强。而和console交互也会占用部分网速,可能影响响应时长。所以线上录制时,务必预留好资源。
回放时,一般建议在线下回放。批量回放对cpu资源占用较高,这个后续优化。
五.坑和注意点
由于jvm-sandbox-repeater刚开源不久,使用时一些坑或者注意点需要了解下。没有详细记录,这里摘要几点。
1)服务要求至少2个CPU,ThreadPoolExecutor中有限制,单核会初始化线程池失败。使用前需要检查下服务器的配置。
2)当post请求同时包含params和body时,代码会只处理body忽略params,导致接口回放响应失败。源代码invokePost函数中处理post请求时,body中没有数据,就取paramsMap组装成FormBody去执行;如果body不为空,就调用invokePostBody(url, headers, body),把paramsMap给丢了。这边简单兼容了下两者并存的情况。
3)官方开源代码的HttpUtil基于okhttp3.OkHttpClient封装的http请求工具中,只支持GET和POST方法,其他的方法回放时会报错。这个估计是开发漏掉了,写起来不难,照着invokeGet和invokePost的实现方法抄一份,请求方法稍微改下就行。
4)回放时,子调用会去匹配mock请求参数的相似度,取相似度100%的来mock。如果子调用参数有当前时间相关的或者随机数等,就会出现匹配不到的情况。这边修改的逻辑是如果没有相似度100%的,就取相似度最高且大于某个值的,否则mock失败,抛出异常,阻断流程。当然不是100%的匹配度,就可能出现返回不是正确响应的mock的情况。这个需要自己权衡。
5)repeater不支持对http返回code的比对,只比对返回的body。回放时对响应不是2xx的会给出异常提示而非原始响应,导致回放比对失败,所以不支持对非2xx响应接口调用的回放使用。至于httpCode的比对,目前看上去是不支持的。
六.总结
jvm-sandbox-repeater是一款便捷好用的流量回放工具。它无侵入、热插拔的特点对于有一堆历史服务的我们来说有着致命的吸引力。由于直接作用在jvm层,它的通用性和可扩展性都不错。
下一步的计划就是支持更多的插件,比如kafka、es等,然后平台化,支持平台操作配置变更、录制、回放、结果查看、历史记录查看等。如此,我们就可以从回归测试的漩涡中解脱出来了。
[原文公众号]