写点什么

流量录制与回放在 vivo 的落地实践

  • 2022 年 2 月 15 日
  • 本文字数:6665 字

    阅读完需:约 22 分钟

一、为什么要使用流量录制与回放?


1.1 vivo 业务状况


近几年,vivo 互联网领域处于高速发展状态,同时由于 vivo 手机出货量一直在国内名列前茅,经过多年积累,用户规模非常庞大。因此,vivo 手机出厂内置很多应用,如浏览器、短视频、直播、资讯、应用商店等都是直面用户的高并发、复杂系统。这些面向用户的系统对使用体验要求非常高,对这些业务的质量保障是重中之重。



1.2 测试痛点


随着我们业务规模和复杂度不断提高,各种问题和挑战随之而来。其中“在业务迭代升级甚至重构时,如何保证系统修改后的原有业务正确性?”是我们正在着手解决的其中一大难题。


简单的业务系统通过常规的自动化测试工具加上人工测试即可解决,对于复杂系统,回归测试将变成一项艰难的工程。以我们推荐系统为例,一个推荐系统承担了数十个推荐场景。如何在修改某个推荐场景的情况下保证不影响其他场景呢?


之前,我们是通过编写自动化测试用例去解决,但是通过人工编写的测试用例存在较多痛点:


  • 测试用例编写难,数据构造难,用户真实的使用行为不容易模拟。

  • 部分代码逻辑通过测试脚本难以验证。例如发送消息无法验证消息内容没有问题。

  • 依靠人工构造用例难以考虑到系统所有场景,容易造成用例遗漏。

  • 随着系统部署复杂度上升,环境维护成本也比较高。


针对这些复杂业务系统在迭代过程中回归测试效率低的问题,我们进行了一些持续性探索。


1.3 方案探索


我们结合 vivo 互联网体系特色对业界一些方案进行了广泛调研和参考并列举了如下要求:新方案要能简单高效,用户无需过多理解就能轻松上手;业务接入成本足够低,能够快速进行回归测试;新方案通用性、扩展性要足够好,能适应不断变更的系统架构。


我们参考了一些头部互联网公司的技术方案,发现流量录制与回放是一个非常好的选择。业界内有不少头部公司基于这种技术取得了不错的进展和落地价值,为我们带来了一些参考和信心。因此,对于流量录制回放,我们进行了一些更加深入的探索和落地,也就是我们的月光宝盒平台。



二、什么是流量录制与回放?


在介绍具体实践之前,先简单介绍一下什么是流量录制与回放?


流量录制回放是通过复制线上真实流量(录制)然后在测试环境进行模拟请求(回放)验证代码逻辑正确性。通过采集线上流量在测试环境回放逐一对比每个子调用差异和入口调用结果来发现接口代码是否存在问题。


利用这种机制进行回归测试具备许多优势:首先,通过录制流量取代测试用例简单高效,易于形成丰富的测试用例;其次,回放线上流量能完美模拟用户真实行为,避免人工编写存在的差异性;另外通过对录制数据和回放数据采用对象对比方式能更深入、细微验证系统逻辑;最后录制的流量无需维护,随取随用,非常方便。


三、月光宝盒平台


流量录制与回放这种创新性的机制理论上是非常优秀的,但是实现起来却不太容易,里面有诸多难题需要解决。下面将给大家介绍流量录制与回放在 vivo 互联网体系的落地方案和遇到的问题以及我们是如何解决这些问题的。


3.1 底层架构


vivo 月光宝盒平台借鉴了开源 Jvm-Sandbox-Repeater 项目经验,在 Jvm-Sandbox-Repeater 基础上做了二次开发和改造。月光宝盒平台包括了服务端和 Java Agent 两大模块,整体架构如下图所示。

3.1.1 业务架构


下图是我们服务端整体业务架构,整个服务端可以划分成任务管理、数据管理、覆盖率分析、配置管理、监控告警等模块。


  • 任务管理模块管理用户的录制和回放任务,包括任务启停、任务进度、任务状态等;

  • 数据管理模块用来管理用户录制与回放的流量数据、以及分析数据;

  • 覆盖率分析模块用来统计用户回归覆盖率指标;

  • 配置管理模块用来配置系统与应用的全局参数;

  • 监控模块用来分析 Agent 各方面性能指标;


此外还有一些消息通知模块。



3.1.2 Agent 架构


下图是 Agent 模块整体架构图,Agent 是流量录制回放过程的核心。Agent 是基于字节码机制实现,整体包括了四层结构:


  • 底层是基础容器层,这一层是标准的 Java-Agent 实现;


  • 容器层上面是依赖层,这一层引入了我们需要的第三方资源、实现了字节码插桩机制、类加载隔离、class 元数据管理能力。


  • 依赖层之上是基础能力层,在这一层实现了基本的原子功能,如录制回放插件的管理、数据管理、数据对比、子调用 Mokc、运行监控、配置加载等能力。


  • 最上层是业务逻辑层,这一层可以将基础逻辑功能组合在一起形成一个完整的业务单元。目前月光宝盒除了支持流量录制与回放外,还支持了类似依赖分析、数据 Mock 等功能。



3.2 月光宝盒的启动流程


录制回放任务启动最重要是能无侵入的将我们 Agent 下发到指定业务机器上并且自动将 Agent Attach 到我们业务进程上去。


月光宝盒的启动流程如下图所示,用户首先在月光宝盒平台配置录制回放任务。完成配置后,配置信息会入库并同时通过 VCS(vivo 自研的作业调度平台)将启动脚本、vivo-repeater-agent 包下发至用户配置的机器上。然后会执行 shell 脚本并拉起 sandbox,将 agent attach 至目标的 JVM。随后 agent 便可以在目标 JVM 上通过反射创建 jvm sandbox,sandbox 会通过 spi 拉起多个模块。


其中最重要是 vivo repeater module,它会通过 spi 加载多个插件,这些插件最终会以 ASM 的方式增强目标 JVM 上的代码,从而实现字节码插桩,而流量的录制与回放便是使用这些增强的插件进行流量拦截、下发存储完成的。



上述的执行流程允许用户仅凭在控制台配置少量信息就能完成复杂的流量录制与回放功能,下面我们将对录制与回放的详细过程进行说明。

3.3 流量录制过程


下面是一个流量录制的过程。一条流量的调用链路包括入口调用和若干次子调用,流量的录制过程就是把入口调用和子调用通过一个唯一 ID 绑定成一次完整的调用记录。月光宝盒找到入口调用和子调用合适的代码点(关键入口和出口),基于字节码插桩技术在该代码点进行代码增强,实现调用拦截,记录调用的入参和返回值,然后根据相应的调用类型(如 dubbo、http)生成一个录制标识。当调用完成时,月光宝盒就采集到了整个流量的调用记录,之后月光宝盒进行数据脱敏、序列化等操作,最后加密发送到服务端进行储存。




录制是一个比较复杂的过程,在这个过程中我们持续踩了一些坑和遇到了一些问题,下面我列举几个比较重要的问题和大家分享一下。


3.3.1 难点一:Full GC


初期,vivo 内部系统在使用月光宝盒时发生了 Full GC 的现象。经过分析得知是录制的接口对 guava 调用非常多,导致录制的请求流量太大从而造成了 FULL GC。这是因为在完成一条接口流量的录制前,录制到的所有数据都在内存中,流量或者子调用一旦过大便容易导致频繁 Full GC。另外还有一些高并发系统接口比较多,同时录制多个高并发接口存在性能压力。因此,我们当时针对月光宝盒的性能问题进行了如下优化:


  • 严格限制并发录制数量、限制单条流量子调用数量;

  • 对录制过程进行监控、异常降级;

  • 合并相同的子调用录制过程以减少子调用数量。

  • 对录制缓存占用进行实时监控,超出警戒线及时进行降级处理。


经过不断优化后,录制过程非常平稳,再也没有出现因为流量过大或者其他问题导致的 Full GC 现象。


3.3.2 难点二:调用链路串联


流量录制和回放存在线程上下文标识,vivo 有不少系统有自定义业务线程池或者使用第三方框架自带了线程池(例如 Hystrix)会导致标识丢失导致无法串联整个调用链路问题。


月光宝盒最开始是依赖 Jvm-Sandbox-Repeater 的基础能力,在未使用线程池时,可以将录制标识存放于 ThreadLocal 中串联整个调用链路;而使用线程池时,我们利用自身 Agent 对 Java 线程池进行自动增强透传我们录制回放标识,但这么做会和公司的调用链 Agent 对线程池的增强产生冲突从而导致 JVM 异常崩溃,这种方式没有办法进行。


最终我们决定与公司调用链团队合作,借助调用链的 Tracer 上下文进行传递录制标识,双方都进行了一定程度改造特别是两个 Agent 对 HTTP、Dubbo 埋点位置进行了一定调整。目前我们还没有解决 ForkJoinPoool 这种线程池框架传递标识问题,后续会继续对这类线程池做支持。


3.3.3 难点三:数据安全


第三个是录制的流量如何保证数据安全,不少系统有一些铭感数据。对此,我们针对录制的数据进行了可配置化的脱敏处理,用户可以在月光宝盒平台配置待脱敏字段,Agent 在录制流量的时候后会根据配置信息在内存中对这些字段进行脱敏处理,保证传输过程和存储过程的数据安全。另外,月光宝盒会严格控制流量详情的查看权限,防止跨项目的数据查询行为。


3.3.4 难点四:流量去重


第四个是流量去重问题。有时业务方在使用月光宝盒平台时可能录制到非常多的相同流量,造成后续回放耗时较长和问题排查效率低下。因此我们针对该现象思考如何能在保证接口覆盖率的情况下尽可能减少相同流量的数量。目前的方案是根据流量入参和执行调用栈作去重操作。在录制时,Agent 会根据去重配置信息进行流量去重操作,保证入库的流量数据是唯一的。通过这种机制在一些场景大幅度减少了录制流量数量,提高了流量的使用效率。


3.4 流量回放过程


下图是流量回放的过程。流量回放是通过获取录制流量的入口调用,再次对迭代后的系统发起调用,然后去验证系统逻辑正确性的过程。和录制不一样的是,回放对于外部的调用都是 Mock 的,这个过程不会真正的去访问数据库。回放过程会将录制子调用和回放子调用的入参进行对比,如果参数不一致那么会阻断回放流量,如果参数一致会使用录制子调用的结果进行 Mock 返回。回放完成同样会产生一个响应结果,这个时候我们会对比原始录制结果和回放响应结果,根据该对比结果和子调用的对比结果就能得出被测试系统的正确性了。



回放是一个更加复杂的过程,因为录制和回放一般在不同环境的不同版本系统执行,可能存在较大差异,如果处理不当回放成功率会比较低。起初接入月光宝盒的应用成功率都比较低,后来经过长期优化和精细化运营,月光宝盒的回放成功率不断提升。下面将分享几个遇到的难点以及一些应对策略。

3.4.1 难点一:时间差异


第一个难点是时间差异的影响,一些系统业务逻辑里面存在和时间有关逻辑,由于录制和回放时间不一样导致不少场景有时间相关逻辑导致回放失败。针对这个问题我们进行了一些研究,并最终将回放时间和录制时间保持一致。


对于 System.currentTimeMillis() 这种 native 方法,Agent 会动态修改方法体的字节码,代理掉业务对该方法的调用,动态替换为平台事先定义的获取时间方法从而保证时间替换。解决了这个问题对于 Date 这些类也就迎刃而解了。另外,JDK8 中的 LocalDateTime 等非 native 时间方法就比较简单了,直接 Mock 掉时间方法调用即可。使用这些机制基本上消除了业务逻辑里面的时间差异问题,消除了因为时间导致的回放失败问题。

3.4.2 难点二:系统降噪


第二个难点是如何处理系统噪音。很多系统里面存在一些 traceId、sequenceId 等通用噪音字段,这些噪音字段也是导致回放失败的因素。初期业务接入时需要逐个排查,整体效率比较低。后来月光宝盒支持了全局级别、应用级别、接口级别的噪音字段配置,很多通用的噪音字段可以直接通过全局配置解决,业务接入只需要个性化配置噪音字段即可,通过这种分层次的降噪配置大幅提升业务接入效率。

3.4.3 难点三:环境统一


第三个难点是环境差异。以 vivo 互联网体系为例,一般在线上环境进行录制,在测试环境和预发环境进行回放,刚开始因为环境不一致导致回放失败的案例非常多,影响了整体回放成功率。针对这个问题,我们进行了一系列探索和解决。月光宝盒在线上录制时会同时录制一份线上环境配置,线下回放时会利用线上配置自动替换掉线下的环境配置,通过这种机制保证了配置中心数据一致性。另外对于一些系统内存性质的配置数据,月光宝盒支持配置接口同步内存数据。通过这些解决方案,我们基本上保证了线上、线下环境的一致性,大幅度减少了因为环境配置导致的回放失败数量。

3.4.4 难点四:子调用匹配


第四个难点是子调用匹配问题。最开始指定的匹配策略无法满足复杂业务场景,经常出现匹不到流量或者匹配错误导致回放难以成功。后面我们针对不同的回放子调用指定不同的匹配策略:缓存类型按照缓存 Key 去匹配;HTTP 类型按照 URI 匹配;Dubbo 按照接口、方法名、参数类型匹配等。另外,如果匹配到多条相同子调用,我们会比较系统调用栈和入参请求参数,结合调用栈和请求参数两个维度寻找最可能匹配流量,通过这些精细化匹配策略提升了匹配成功率。

3.4.5 难点五:问题排查


第五个难点是问题排查了,录制和回放是非常复杂的过程,由于 Agent 运行在业务端机器出现任何问题去分析排查难度都很大。为了提升排查效率我们支持了若干手段:

1)、支持回放分析调用链路图,下面会详细讲解;

2)、任务启动详细命令和参数输出,通过输出任务启动命令参数,我们在本地非常方便启动和模拟线上运行的录制和回放任务,提高了排查效率。

3)、本地一键安装 Agent,在本地修改了 Agent 代码后我们一键就可以将新的 Agent 在本地远程测试环境安装。


除了这些功能外我们还开发了很多效率工具,这里就不一一展开说明了。


3.5 丰富的协议支持


vivo 业务种类非常多,不同业务技术栈有差异。这些系统接入我们平台需要针对性的适配相应插件。通过我们不断完善插件,目前我们已经支持了下面数十种插件,基本已经覆盖了各种常见的中间件。



3.6 月光宝盒平台其它特点

3.6.1 可视化调用链路


起初,业务回放失败只能依靠平台开发人员协助排查,整个排查过程费时费力。针对这种情况,我们提供了一些可视化的运维工具。其中一个就是链路调用分析图。我们对录制和回放过程进行详细跟踪和记录,通过调用链路图帮助用户分析执行过程。出现问题时,用户可以清晰看到具体异常位置和根本原因,提高了排查效率。



3.6.2 回归代码覆盖率


月光宝盒的优势之一是具备很高的流量覆盖率很容易形成高覆盖率。如何去验证回放的流量确实覆盖了业务系统的各个场景,让使用人员使用了月光宝盒就不存在疑虑放心大胆的上线。


针对这个问题,月光宝盒提供了代码回归覆盖率的统计能力,我们利用内部的 CoCo-Server 平台统计了系统全量覆盖率和增量代码覆盖率。为了识别覆盖率数据来自流量回放,我们在回放前需要调用接口清理机器内存里面的覆盖率数据,这种方式可能存在和其他流量冲突可能性,后续等 CoCo-Server 平台完成流量染色区分流量来源就没有这方面担忧了。



3.6.3 定时录制与回放


虽然流量录制和回放的操作流程已经非常简便了,但对于一些频繁使用的业务人员来说仍然较为繁琐,特别是有些版本涉及了过多的系统,同时录制回放多个系统效率比较低下。为了提高使用效率,月光宝盒支持了用户自定义定时录制、回放任务的能力。通过定时任务可以批量定时录制和回放,减小了人工操作成本,提高了平台使用体验。



3.7 月光宝盒其他应用


除了自动化测试,我们在其他方面也进行了一些探索和应用。第一个是流量压测,用户可以通过月光宝盒平台分析录制的流量生成的压测模型。第二个是问题定位,使用月光宝盒平台在线下回放重现线上问题,帮助测试、开发人员复现问题现场。最后一个是安全分析,常态化录制测试环境流量可以帮助安全工程师提供流量素材,识别业务系统安全风险等。


四、核心指标


月光宝盒平台的接入非常简单,业务初次接入基本上 10 分钟内就可以完成。平台上线不到一年就累计接入了近 200 个业务系统,很多都是 vivo 互联网体系中最为核心的应用。一年以来累计完成 1W+次录制与回放。接入月光宝盒后,平台提前发现了不同业务的累计数十个线上问题,有效减少线上事故发生次数。在很多场景下,使用月光宝盒平台的流量录制回放功能可以提升测试、开发人员 80%以上的工作效率,总体来看超出了我们的预期目标。



五、未来规划


在未来规划上我们主要聚焦在两方面,一个是功能规划,第二个是协同开源。


5.1、功能规划


目前我们完成了平台基础功能建设,但是还存在使用效率等问题,后续我们重点会在下面两个方向去优化:


1)希望能够实现精准化测试,避免每次全量回放录制数据,进一步降低回放耗时。精准化测试需要分析变更代码,获得变更代码的影响范围,然后基于此筛选出对应的流量进行回放,这样就能减小回放覆盖范围。


2)是希望能够与 vivo 互联网体系下的 CI/CD 结合,当业务系统发布到了预发环境后,能够自动触发录制和回放任务。这样在上线前能给系统进行一些风险识别同时提升用户使用效率。


5.2 开源共创


开源是未来软件的发展趋势,对于开源,我们一直是受益者,我们也期望能积极参与到开源项目为社区贡献力量。我们参与了开源https://github.com/alibaba/jvm-sandbox-repeater项目,成为了社区核心贡献者。第一期累计贡献了 5 个社区没有但是比较重要的插件。后续我们准备继续按照下面规划逐步向社区回馈月光宝盒的一些核心能力。



作者:vivo 互联网服务器团队-Liu YanJiang、Xu Weiteng


本文根据 Liu YanJiang 老师在“2021 vivo 开发者大会"现场演讲内容整理而成。公众号回复【2021VDC】获取互联网技术分会场议题资料。

发布于: 刚刚阅读数: 2
用户头像

官方公众号:vivo互联网技术,ID:vivoVMIC 2020.07.10 加入

分享 vivo 互联网技术干货与沙龙活动,推荐最新行业动态与热门会议。

评论

发布
暂无评论
流量录制与回放在vivo的落地实践