《流量回放技术揭秘》

引言

本文主要介绍一种流量回放技术,通过采集底层流量数据,使用创新的Mock方式将数据应用在功能回归测试场景,更好的保障业务迭代质量。

应用场景

  • 日常上线回归测试:确保每次上线核心流程不受影响,可以接入pipeline流水线。
  • 开发重构类需求:微服务拆分、框架升级、组件升级等。

应用特性

  • 线上流量测试:更真实、更全面、测试用例维护成本更低。
  • 支持写接口回放:所有下游调用都会被mock返回,不会真实写入数据库,不会污染应用数据。

一、背景

1.1、背景

需求改动难以评估影响面,这是很多业务面临的痛点问题,接下来介绍两个真实案例。

1、真实案例一:组件升级,导致线上服务查询超时。

  • 原因:老版本组件查询数据库时会自动类型转换(int -> string),新版本不会,导致查询没命中索引。如果下图所示,如果id字段在数据库是字符串类型,组件升级前会命中索引,组件升级后不会。

2、真实案例二:框架升级,引发多起工单问题。

  • 原因 :Laravel框架由5.5.44升级到5.5.48,Cache设置有效期参数由分钟改成了秒,官方无任何发布说明。(参考:Laravel 5.5.48 的巨坑

小结:以上案例常见测试手段都无法提前发现问题,故障复盘的时候没有很好的改进措施。

1.2、开源方案

常见开源的回归测试解决方案,有:tcpcopygoreplaydiffy等,适合性能测试(真实流量压测)、适合只读接口功能回归测试等。

常见开源方案存在以下痛点问题:

1、不支持写接口回放:写接口存在非幂等性,有脏数据风险。

  • 非幂等性:写接口每次回放结果可能不一样,比如:第一次回放提示写入成功,第二次回放可能提示主键冲突,每次回放差异巨大没法对比测试。
  • 有脏数据风险:回放的时候会真实写入数据库,可能会因为你的测试导致数据库存在很多脏数据。

2、依赖下游测试环境:依赖下游服务和下游数据源。

  • 依赖下游服务:下游服务必须稳定才能进行测试,随着微服务的拆分这可能会是一个挑战。
  • 依赖下游数据源:线上日志获取的请求在线下测试可能行不通。比如:同一个用户ID线上有数据,线下测试可能没有数据,因为依赖的数据源不一样。

以下列举几个支持写接口回放的开源解决方案,后续我们实现的时候会重要参考。

  • rdebug:滴滴开源的php解决方案
  • sharingan:滴滴开源的go解决方案
  • jvm-sandbox-repeater:阿里开源的java解决方案,MTSC 2019 年度开源贡献奖。

二、原理

2.1、基本原理

基本原理:录制服务线上流量,通过回放对比发现潜在问题。先流量录制,再流量回放。

1、流量录制:上图绿色背景部分,以线上网络流量采集为主。

  • 思路:不仅录制接口请求和返回(上图1和8),还录制接口所有子调用请求和返回(上图2到7),将这些流量(上图1到8)都存储起来。
  • 难点:如何采集线上服务网络流量?(技术点:流量拦截)、如何将网络流量串联起来?(技术点:链路追踪

2、流量回放:上图红色背景部分,以线下网络流量对比为主。

  • 思路:回放过程中,会保持所有输入一致(含子调用返回值),对比所有输出的差异(含子调用请求差异),噪音、时间等干扰因素会被排除。
  • 难点:如何重定向到Mock Server?(技术点:连接重定向)、Mock Server如何匹配流量返回?(技术点:流量匹配

3、原理应用:各语言实现方案有差异,本文主要介绍PHP和JAVA实现。

  • PHP语言:主要参考开源的解决方案,做了部分改进,如:平台化改造、并发回放支持、存储优化等。
  • JAVA语言:基于上述原理创新实现,首个基于网络层流量实现写接口回放的方案。

2.2、PHP开源方案

PHP开源方案实现用到了动态库加载技术,先做个简单的介绍。

2.2.1、动态库加载技术

  • 动态库加载技术:使用LD_PRELOAD优先加载动态链接库。LD_PRELOAD是一个Linux环境变量,允许程序运行前优先加载动态链接库(可以用来写游戏外挂)。
  • 动态库加载应用:如何知道应用程序在执行期间打开了那些文件?(方案之一:可以通过重写libc open函数动态加载来实现)

2.2.2、流量录制实现

1、流量拦截:获取上图1、2、3、4流量。

  • 方案:通过动态库加载技术,重写libc的read/write函数完成。PHP应用所有网络调用都会经过libc,重写网络流量读写函数可以获取网络流量。

2、链路追踪:串联上图1、2、3、4流量。

  • 方案:利用语言特性,通过线程ID串联。PHP请求通常在一个线程内串行完成,通过libc拦截流量很容易获取线程ID。(优势:无侵入)

2.2.3、流量回放实现

1、连接重定向:所有下游调用连接重定向到Mock Server ,由Mock Server匹配线上录制流量返回。

  • 方案:通过动态库加载技术,重写libc connect函数完成 。(修改连接地址到统一的Mock Server)

2、流量匹配:Mock Server收到下游调用请求,会尽可能的匹配线上录制的流量,并给出相应返回值。

  • 方案:将回放请求按照固定大小切分(默认16字节),分别和线上录制的请求匹配打分,取得分最高的流量。
  • 打分:包含最小单元数据得1分,不包含最小单元数据得0分,噪音等因素会干扰。

2.3、JAVA创新方案

2.3.1、流量录制实现

1、方案对比:和PHP开源方案对比

  • 共同点流量拦截方式一样,都可以在libc层获取流量。底层网络调用都通过libc来完成,通过libc层拦截,可以获取所有网络调用流量。
  • 差异点链路追踪方式不一样,JAVA涉及多线程。

2、方案实现:分场景实现有差异

  • BIO场景 :常见http、mysql等下游调用,和接口请求在同一个线程,无需特别处理可以串联流量。(方案:在libc层拦截和串联流量

  • NIO场景 :常见redis、dubbo等下游调用,和接口请求不在一个线程,需要通过字节码增强技术拦截上报流量。(原理:同skywalking

  • 录制示例 :以NIO场景的Redis letture组件为例,我们和skywalking基于同样的拦截点(如图2-3-1),同样是基于Command指令(如图2-3-2),只是获取的信息不同(Skywalking方案:获取指令,如:GET foo;我们方案:获取网络字节流,如:*2 $3 GET $3 foo)。

2.3.2、流量回放实现

1、方案对比:和PHP开源方案对比

a、共同点:绝大部分能力可以复用

  • PHP和JAVA语言流量回放都在统一平台进行,绝大部分能力可以复用。可复用核心能力:连接重定向流量匹配、时间回退、协议解析、噪音去除、流量染色等。

b、差异点:部分场景需要特殊处理。

2、方案实现:本地缓存场景示例

a、流量录制:扩展协议,不限网络流量录制

  • 流量拦截:通过字节码增强技术,拦截本地缓存方法调用,序列化后存储。(已支持GuavaCache 、CaffeineCache组件 )
  • 链路追踪:本地缓存一般都是同步调用,不需要特别处理,根据接口所在主线程串联即可。

b、流量回放:构造请求,统一流量匹配返回

  • 连接重定向:没有网络调用,需要构造TCP请求,和其他请求一样通过Mock Server获取录制流量。
  • 流量匹配: Mock Server按照统一的策略打分匹配返回即可,核心流程不变。

2.3.3、方案对比

和开源阿里方案对比:解决问题场景一样,比较知名的是:jvm-sandbox-repeater

1、优势

a、插件维护成本更低。

  • 我们方案:方案更偏底层,很多组件可以复用。如:同步调用的组件(如: mysql、http 等),统一都在socket包拦截。
  • 阿里方案:方案偏应用层,不同组件无法复用。组件都需要单独实现,可能还需要区分版本,很多团队尝试后放弃根因。

b、回放流程可以通用。

  • 我们方案:不同语言大部分能力都可以复用,能做到体验一致。
  • 阿里方案:仅支持JAVA语言。

2、不足

a、部分场景不支持:只支持无状态协议回放,有状态协议不支持,如:https、grpc等。

  • 方案:部分含不支持协议的接口可以设置过滤,常见协议都支持, 如:http、redis、mysql 、dubbo等

b、可读性相对较差:我们方案使用网络流量录制和回放,相对可读性差,不利于后续噪音去除和定位问题。

  • 方案:需要解析协议,可以直接复用开源PHP/GO解决方案。如:mysql协议,只展示其中sql语句即可。

2.4、小结

  • 整体思路:分语言录制,统一平台回放。先流量录制(线上为主,网络流量采集为主),再流量回放(线下为主,网络流量对比为主)。
  • 流量录制:不同语言实现方式有差别,需单独实现(PHP利用语言特性在libc层就可以实现,JAVA有异步场景需要特殊处理)。
  • 流量回放:不同语言实现方式大同小异,可以复用(核心能力都可以复用,包含:连接重定向、流量匹配、时间回退、协议解析、噪音去除等)。

三、结语

如果你对流量回放原理和应用感兴趣,欢迎点赞、收藏、评论(必回)、转发~

相关推荐
苏三的开发日记17 分钟前
linux搭建hadoop服务
后端
sir76132 分钟前
Redisson分布式锁实现原理
后端
大学生资源网1 小时前
基于springboot的万亩助农网站的设计与实现源代码(源码+文档)
java·spring boot·后端·mysql·毕业设计·源码
苏三的开发日记1 小时前
linux端进行kafka集群服务的搭建
后端
苏三的开发日记1 小时前
windows系统搭建kafka环境
后端
爬山算法2 小时前
Netty(19)Netty的性能优化手段有哪些?
java·后端
Tony Bai2 小时前
Cloudflare 2025 年度报告发布——Go 语言再次“屠榜”API 领域,AI 流量激增!
开发语言·人工智能·后端·golang
想用offer打牌2 小时前
虚拟内存与寻址方式解析(面试版)
java·后端·面试·系统架构
無量2 小时前
AQS抽象队列同步器原理与应用
后端
9号达人2 小时前
支付成功订单却没了?MyBatis连接池的坑我踩了
java·后端·面试