背景
最近在公司开发基于JVM的“流量录制回放的工具”,简单理解是把生产环境用户的请求
到响应
包括过程中的含状态子组件
,把整个过程入参和返回值录制下来,到测试环境一一回放Mock,达到测试接口子调用覆盖率,上线前不同代码版本运行情况对比等目的,提供更多信息帮助测试和开发同学提高系统健壮性。市面同行的类似方案
简单流程:
拦截JVM的方案,使用了阿里的 jvm-sandbox , 原理是attach(或agent形式启动)到运行中的JVM,利用Java提供的InstrumentationApi
,加载自定义模块,关注对应的拦截点,重新编织字节码到运行中的程序里。
系统的其他部分,也引入了同样是阿里的Arthas Java工具,主要用到一些trace方法,反编译代码等功能。
问题
就工程复杂度来看,线上录制
相对复杂度低,主要是系统间的调用,稳定性(过程中不影响正常业务),在运行一段时间考虑如何把流量逐一落盘存储即可。而到线下回放Mock
的过程,则会遇到各种的问题,例如机器性能的差异, 组件不完整,网络问题等,其中最大的问题是,和录制环境状态的差异。
尽管目前工具拦截了的子调用组件(都是状态相关)已覆盖了日常大部分应用,但实际开发同学实施代码有各种不同的习惯与个例,回放时即时同样的代码,依然会导致有各种执行结果,例如:
- 子调用组件录制不全
- 直接使用本地缓存例如
ConcurrentHashMap
等 - 线上录制的一些时间参数与回放时不一致
- 调用一些非状态相关的ABTest SDK导致走向不同的代码路径
- 直接代码里写死与环境绑定的逻辑
- 配置中心不一致
....
目前对回放结果的调优,参数/响应
用了JsonPath
的方式去忽略噪音,提高了命中率
例如:
// 入参例子
// 录制到时间是昨天, 回放时把第一个参数忽略不加入匹配
userMapper.selectListGreaterThanDateAndNicknameEq(new Date(), "jas");
// 响应例子
// 调用下游系统,例如个性化推荐,每次返回的结果长度不一样, 影响到下一个调用的入参或最终接口实际响应结果
otherRecommandService.getRecommandList(userId)