本文作者陈恒捷是TesterHome社区主编,第十届MTSC大会上海站-开源专场出品人。先后在PP助手、PPmoney、荔枝等公司从事测试效能提升相关工作,在测试技术及效率提升方面有丰富的经验积累。
为了避免文章过长,此文单独记录 repeater-console 部分的使用。对于 jvm-sandbox-repeater 普通用法的尝鲜记录,请参照 通用流量录制回放工具 jvm-sandbox-repeater 实践记录 (一)
repeater-console 简介
官方的说明:
jvm-sandbox-repeater 仅仅提供了录制回放的能力,如果需要完成
业务回归
、实时监控
、压测
等平台,后面须要有一个数据中心
负责采集数据的加工、存储、搜索,repeater-console 提供了简单的demo示例;一个模块管理
平台负责管理JVM-Sandbox各模块生命周期;一个配置管理
平台负责维护和推送jvm-sandbox-repeater采集所须要的各种配置变更在阿里集团淘系技术质量内部,已有一套完整的体系在持续运行,从17年开始支持了淘系技术质量部的CI、建站、系统重构等多方面质量保障任务,后续如有需要也会考虑把更多的东西开源回馈社区
注意:目前项目代码默认启动standalone模式,不需要依赖任何服务端和存储,能够简单快速的实现单机的录制回放,控制单机模式的开关在
~/.sandbox-module/cfg/repeater.properties
文件中的repeat.standalone.mode=true //开启或关闭单机工作模式
,关闭单机模式后,配置拉取/消息投递等都依赖repeater.properties中配置的具体url;如不想通过http拉取和消息投递的也可以自己实现Broadcaster
和ConfigManager
。稍后我们会公布一份录制回放所需的完整架构图以及jvm-sandbox-repeater在整个体系中的位置供大家工程使用做参考。
个人理解,要想在业务中使用,我们还得搞下 数据中心、模块管理 和 配置管理 。
【数据中心】:你存了那么多流量,总得有个存储和管理的地方吧,数据中心就是干这个活。要不光靠官方提供的那个透传 repeatId 的回放方法,只能回放单个流量,实际项目不够用。
【模块管理】:这个还不是太了解,个人理解是各个 plugin 的管理?
【配置管理】:就是之前试用时说过的只有一个 ~/.sandbox-module/cfg/repeater-config.json
配置文件,是不可能满足多个项目同时使用的需要的。所以需要有个配置管理,提供这方面配置的存储和修改能力。
源码熟悉
由于目前官方对于这个 console 只有一份非常简单的文档:
repeater-console工程集成录制/回放的配置管理;数据存储/数据对比等具备多种能力,因各系统架构差异较大,目前仅开源简单的demo工程,后续会提供统一的工程,也希望有能力和时间的同学来提PR
curl -s http://127.0.0.1:8001/regress/getAsync/repeater -H
'Repeat-TraceId:030010083212156034386424510101ed'
curl -s http://127.0.0.1:8001/facade/api/repeat/repeater/030010083212156034386424510101ed -H "RepeatId:030010083212156034386424510201ed"
curl -s http://127.0.0.1:8001/facade/api/repeat/callback/030010083212156034386424510201ed
所以只能通过解读源码来反推用法咯。
个人的源码阅读三步骤:明确阅读目的、了解整体架构、细读目标功能
step 0 明确阅读目的
目的很简单,使用 repeater-console ,在目前的 demo 项目上完成批量流量录制回放的功能
step 1 了解整体架构
为了便于描述,还是用 tree 吧。
特别说明:以下均为个人分析,并不保证正确哈。
tree -L 10 | grep -v iml | grep -v target
.
├── Readme.md
├── pom.xml
├── repeater-console-common // 存放公共方法的模块
│ ├── pom.xml
│ └── src
│ └── main
│ └── java
│ └── com
│ └── alibaba
│ └── repeater
│ └── console
│ └── common
│ ├── PackageInfo.java // 一个空的类,应该是预留用的
│ └── domain // 目前只有一个名为 Regress 的 java bean ,代表单条回放记录
├── repeater-console-dal // 和数据库打交道的存储模块,model 层
│ ├── pom.xml
│ └── src
│ └── main
│ ├── java
│ │ └── com
│ │ └── alibaba
│ │ └── repeater
│ │ └── console
│ │ └── dal
│ │ ├── mapper // mybatis 的 mapper 映射类,存放数据库操作犯法
│ │ └── model // mybatis 的 model 类,和数据库表结构对应
│ └── resources
│ └── database.sql // 数据库初始化语句
├── repeater-console-service // 主要逻辑实现的模块,service 层
│ ├── pom.xml
│ └── src
│ └── main
│ └── java
│ └── com
│ └── alibaba
│ └── repeater
│ └── console
│ └── service
│ ├── RecordService.java // 存储服务,提供存储录制、存储回放、获取记录、执行回放、查看回放结果接口的定义
│ ├── RegressService.java // 回归服务,提供获取单个回放、多个回放、找到你的小伙伴、slogan喊口号4个接口的定义(最后两个接口不知道是什么鬼。。。)
│ ├── impl
│ │ ├── AbstractRecordService.java // 存储服务一个抽象实现,提供了 repeat 方法和 jvm-sandbox-repeater 进行交互,触发回放
│ │ ├── RecordServiceLocalImpl.java // 存储服务的本地存储实现。使用一个 ConcurrentHashMap 把所有数据存到内存中。
│ │ ├── RecordServiceMysqlImpl.java // 存储服务 mysql 存储的实现。使用前面存储模块和 mysql 数据库交互,进行存储。
│ │ ├── RecordServiceProxyImpl.java // 存储服务的代理类,根据配置文件值来决定用哪个实现类进行存储服务的实现
│ │ └── RegressServiceImpl.java // 回归服务的实现类。包含了官方提供的 slogan 服务的实现。
│ └── util
│ └── ConvertUtil.java // 给原始录制记录加上一些元数据(如 appName,environment 等),并转换成一个完整的录制记录的工具类。转换方法目前各个存储服务用的都是 hessian 序列化。
├── repeater-console-start // 最外部的层,controller 层。直接暴露接口和提供 main 入口。我们最前面 slogan 示例看到的 repeater-bootstrap.jar 包,实际就是用这里源码打出来的包。
│ ├── pom.xml
│ └── src
│ ├── main
│ │ ├── java
│ │ │ └── com
│ │ │ └── alibaba
│ │ │ └── repeater
│ │ │ └── console
│ │ │ └── start
│ │ │ ├── Application.java // 标准的 spring boot 启动类
│ │ │ ├── ConfigurationBean.java // java 回放用的感知 spring context 的 hook
│ │ │ └── controller
│ │ │ ├── ConfigFacadeApi.java // 配置管理服务 api 设计的示例。仅提供了获取配置的方法,而且直接 hard code 了一份配置。
│ │ │ ├── RecordFacadeApi.java // 存储服务 api 设计的示例,提供了存储录制、存储回放、获取记录、执行回放、查看回放结果五个 api 接口
│ │ │ └── RegressController.java // 回归服务,相当于一个示例的被测服务。官方的 slogan 例子用的就是这里的接口。
│ │ └── resources
│ │ └── application.properties // 配置文件。需要留意的是,里面有个 `repeat.repeat.url` 配置项,需要和 sandbox 的监听 port 保持一致。
│ └── test
│ └── java
│ └── com
│ └── alibaba
│ └── repeater
│ └── console
│ └── start
│ └── RegressTest.java // 一个自动化集成测试用例,如果在 idea 里面跑的话,需要先手动启动 console 服务才能运行,且测试了下,全部用例都是 fail 的。先忽略。
简单小结:
1、console 划分为了4个子模块,除了一个是公共模块外,剩余三个分别是数据层、service逻辑层和最外部的 controller 层,基本是一个标准 spring boot 程序。
2、里面主要提供了3个服务:存储服务,配置管理服务,回归服务(本质上就是个示例,估计是给自动化测试用的)
3、需要重点关注的是存储服务,里面包含了存储录制、存储回放、获取记录、执行回放、查看回放结果五个 api 接口。
step 3 细读目标功能
从上一步已经明确了,目标功能是存储服务。因此进一步细看对应的代码。主要关注存储服务的实现。为了简便理解,主要针对 local 这个本地存储的实现进行解读。
里面涉及几个 model 定义,为了方便理解,先说明下:
RecordWrapper
: repeater 提供的一个完整的录制记录。包括appName、环境名、主机名、traceId、入口描述、入口调用记录、子调用记录。RepeatModel
: repeater 提供的一个回放结果记录。包括 repeatId、是否完成、实际返回值、原始返回值、diff记录、耗时、traceId。Record
: console 提供的单个录制记录的描述,包括创建时间、录制时间、appName、主机名、traceId、原始录制记录。用途估计是后续用来过滤筛选记录。
下面的解读主要涉及上述3个类,更详细的领域模型划分,建议参考 domain
- 添加录制的记录
@Override
public RepeaterResult<String> saveRecord(String body) {
try {
// 把输入值反序列化成 RecordWrapper 对象
RecordWrapper wrapper = SerializerWrapper.hessianDeserialize(body, RecordWrapper.class);
// 如果反序列化失败,直接返回错误
if (wrapper == null || StringUtils.isEmpty(wrapper.getAppName())) {
return RepeaterResult.builder().success(false).message("invalid request").build();
}
// 把 wrapper + 原始传入的 body ,组合成 record 。主要是添加了一个创建日期、大部分 wrapper 和 record 一一对应地存储,以及把整个 body 放到 wrapperRecord