流量回放上的调研

什么是流量复制?

我们把用户访问系统造成的数据传输定义为流量,那么在用户访问系统的过程中,我们可以把进入和流出的数据复制下来,进行保存,待后续使用,即离线模式,或者转发到一个新的服务器,立即使用,即在线模式。

什么是流量回放?

获取到复制下来的流量以后,我们按照接收的时间顺序,将它们一条一条的传输到待测试的服务中,让测试服务产生相应的响应;相当于实际用户帮助我们进行测试。

通常有以下几种回放测试的情景:

(1)复制下来什么内容就回放什么内容,即全量回放;

(2)复制下来的内容进行一些预设规则的过滤,或者特殊的处理后,再进行回放,即选择性回放;

(3)复制下来的内容,对其进行处理从中获取必须的数据项,比如搜索词,即关键词回放。

          

        

  • 流量控制和管理中心:主要负责流量的调度和管理,包括从哪个线上服务去抓取实时流量,离线流量保存到哪里,从哪里获取,流量需要回放到哪里等相关的任务。
  • 流量复制服务:主要负责从线上服务中复制流量,做到随用随复制,不需要的时候保持静默,不损耗系统资源。
  • 流量处理服务:主要负责对线上复制的流量进行处理,这里包括离线处理和实时处理两大类,主要是过滤掉不需要进行回放的接口,比如写库类型的接口,如果直接回放到测试服务中,也会进行对应的写库操作,如果正好共用同一套数据库,就会造成大量脏数据,甚至对整体的业务流程产生影响。
  • 流量回放服务:接收处理过的流量,进行回放,支持并行产生大量并发的流量,达到压测的目的。
  • 流量跟踪和数据持久化中心:从流量复制到流量回放,整个过程跟踪,以及产生的请求和响应数据,比如压测的实际效果,QPS、CPU等数据,均需要进行持久化保存,进行分析,及时发现问题

    探索如何利用流量回放,将线上真实的数据流转化为覆盖全面的回归测试用例。在调研了业内比较知名的流量回放方案,如 TcpReplay,TcpCopy 后。发现这些方案均只是服务端入口流量的 Copy 组件,虽然的确能将线上流量「复制 & 引流」至目标服务,如果仅仅是复制 HTTP 入口的流量,那么接收流量回放的服也要配套和被流量制服务业务数据一致的存,数据,第三方服

    能录制/回放应用调用链路入口(通常为 HTTP)的 Request/Response

    能录制/回放应用调用链路内部对的 DB,Redis 及其他服务的 Request/Response。

    能串联整个调用链路期间所有相关的录制/回放(一般都是考虑基于 Trace)

    能无限回放至任意环境(包括线上,线下,指定主机等)应用代码无侵入及录制过程对服务极低的性能耗损  。

    除了入口 HTTP Request/Response 处于链路的首位和末尾外。调用链路在应用内部的顺序是不确定的。比如可能先调用 DB 后调用 Redis,也可能先调用 Redis 后调用同样的 DB SQL 多次,是一个没有任何规律(也无法推测出规律)的调用顺序。

   所有录制的 Endpoint 都会同时被录制一个基于当前上下文的调用编号。由于回放时的调用链路与录制时的调用链路是一致的,所以可以通过编号准确的找到当前步骤需要回放的 Endpoint 及其数据。JVM-SANDBOX 来进行 AOP 方式的录制,看起来的确是切实可行的。

jvm-sandbox-repeater

jvm-sandbox-repeater框架基于JVM-Sandbox,具备了JVM-Sandbox的所有特点封装了以下能力:

1.录制/回放基础协议,可快速配置/编码实现一类中间件的录制/回放

2.开放数据上报,对于录制结果可上报到自己的服务端,进行监控、回归、问题排查等上层平台搭建

基于它,我们可以在业务系统无感知的情况下,快速扩展 api ,实现自己的插件,对流量进行录制,入口请求(HTTP/Dubbo/Java)流量回放、子调用(Java/Dubbo)返回值Mock能力。详细介绍可以看官方说明。

录制回放主要原理如下:

repeater作为sandbox一个子模块加载,进行流量录制发送console,console存储流量并且,调用repeater将流量在目标jvm上回放。

在这里插入图片描述

录制:如图,当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进行处理和保存。

 

通过对 DB(MyBatis),Redis(RedisTemlate)等进行 AOP 拦截;

在相关 Endpoint 进行网络交互前记录(序列化)请求,并在网络交互后记录响应;

在请求/响应/及上文提到的调用编号均完备的情况下,使用 Json 序列为包含元数据(比如 Class 信息,数组或者集合的元素类型等)的字符串后推送至消息中间件(如 Kafka);repeater 是通过http发送同步请求(不过有内存队列)。随后通过 Repeat Service 异步消费后存入 DB。

流量录制回放代码入口

流量录制回放配置

@RequestMapping("/facade/api")
public class ConfigFacadeApi {

    @RequestMapping("/config/{appName}/{env}")
    public RepeaterResult<RepeaterConfig> getConfig(@PathVariable("appName") String appName,
                                                    @PathVariable("env") String env) {

        // 改为了可以适用于 gs-rest-service(自己的应用) 的配置
        RepeaterConfig config = new RepeaterConfig();
        List<Behavior> behaviors = Lists.newArrayList();
        config.setPluginIdentities(Lists.newArrayList("http", "java-entrance", "java-subInvoke", "mybatis", "ibatis"));
        // 回放器
        config.setRepeatIdentities(Lists.newArrayList("java", "http"));
        // 白名单列表
        config.setHttpEntrancePatterns(Lists.newArrayList("^/greeting.*$"));
        // java入口方法
        behaviors.add(new Behavior("hello.GreetingController", "greeting"));
        config.setJavaEntranceBehaviors(behaviors);
        List<Behavior> subBehaviors = Lists.newArrayList();
        // java调用插件
        config.setJavaSubInvokeBehaviors(subBehaviors);
        config.setUseTtl(true);
        return RepeaterResult.builder().success(true).message("operate success").data(config).build();
    }

}

 

代码repeater-module负责流量的录制工作,我们从repeater-module模块中,RepeaterModule.class来进行分析。
RepeaterModule实现了ModuleLifecycle接口,在sandbox进行模块加载时,对于实现该接口的实例会一次执行其onLoad方法、onActive方法、onCompleted方法,一起来看下onCompleted方法:

  1. 在线程池中执行,配置拉取,pullConfig实际上就是调用console提供的接口能力
  2. 根据配置进行初始化
  3. 启动心跳检测
public void loadCompleted() {
        ExecutorInner.execute(new Runnable() {
            @Override
            public void run() {
                configManager = StandaloneSwitch.instance().getConfigManager();
                broadcaster = StandaloneSwitch.instance().getBroadcaster();
                invocationListener = new DefaultInvocationListener(broadcaster);
                RepeaterResult<RepeaterConfig> pr = configManager.pullConfig();
                if (pr.isSuccess()) {
                    log.info("pull repeater config success,config={}", pr.getData());
                    ClassloaderBridge.init(loadedClassDataSource);
                    initialize(pr.getData());
                }
            }
        });
        heartbeatHandler = new HeartbeatHandler(configInfo, moduleManager);
        heartbeatHandler.start();
    }

 

repeater代码录制回放流程

   

 

 

 

 

参考   https://developer.aliyun.com/article/710464

 

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值