开源测试工具 [jvm-sandbox-repeater 学习笔记][入门使用篇] 4 录制、回放与调试

ELes for PPmoney · 2019年10月20日 · 最后由 __jack 回复于 2021年12月03日 · 7628 次阅读

本章主要讲述使用在非 standalone 模式下如何进行录制和回放,以及总结了我在学习过程中的调试的技巧。

系列文章导航:
[jvm-sandbox-repeater 学习笔记][入门使用篇] 1 安装与启动
[jvm-sandbox-repeater 学习笔记][入门使用篇] 2 配置说明
[jvm-sandbox-repeater 学习笔记][入门使用篇] 3 现有接口说明
[jvm-sandbox-repeater 学习笔记][入门使用篇] 4 录制、回放与调试(本文)

4.1 开始录制

当 repeater 启动成功后,就会自动开始录制采集。

在非 standalone 模式下,会将录制的结果上发到 repeater-console 中。

4.2 查看录制记录

repeater-console 没有提供可以查看录制记录数据的接口。

如果想要知道更加详细的信息,从3 接口文档的篇章中,只有一个供 repeater 调用的获取录制记录的接口。在该接口中打断点,即可看到更加详细的录制记录信息。

@Override
    public RepeaterResult<String> get(String appName, String traceId) {
        Record record = recordMapper.selectByAppNameAndTraceId(appName, traceId);
        // 在此处打断点,然后调试中执行SerializerWrapper.hessianDeserialize(record.getWrapperRecord(), RecordModel.class)即可查看详情
        if (record == null) {
            return RepeaterResult.builder().success(false).message("data not exits").build();
        }
        return RepeaterResult.builder().success(true).message("operate success").data(record.getWrapperRecord()).build();
    }

4.3 执行回放

参考3 接口文档中,用户可以通过传入录制记录 appName 和 traceId 触发一个录制的回放。

回放成功的要素是:

1、填写正确的 appName、traceId

2、回放时录制回放配置与录制时保持一致。

4.4 查看回放结果

参考3 接口文档中,用户通过回放任务的 repeatId 获取回放结果。

回放结果解读

以下为回放结果的返回结果示例,想要具体字段说明可以参考3 接口文档

{
    "success": true,
    "data": {
        "repeatId": "192168015073156871042776710028ed",
        "finish": true,
        "response": "123",
        "originResponse": "123",
        "diff": [],
        "cost": 179,
        "traceId": "192168015073156871042791710006ed",
        "appName": "redis",
        "environment": "local",
        "host": "192.168.15.73",
        "starTime": "2019-09-17T08:53:47.917+0000",
        "endTime": "2019-09-17T08:53:48.096+0000",
        "mockInvocations": [
            {
                "index": 1,
                "traceId": "192168015073156871042791710006ed",
                "repeatId": "192168015073156871042776710028ed",
                "success": true,
                "skip": false,
                "cost": 11,
                "originUri": "mybatis://UPDATE/org.spring.springboot.dao.CityDao.updateCity",
                "currentUri": "mybatis://UPDATE/org.spring.springboot.dao.CityDao.updateCity",
                "originArgs": [
                    [
                        {
                            "cityName": "温岭市3",
                            "description": "BYSocket 的家在温岭。",
                            "id": 1,
                            "provinceId": 1
                        }
                    ],
                    {
                        "keyProperties": [
                            "id"
                        ],
                        "isUseGeneratedKeys": false
                    }
                ],
                "currentArgs": [
                    [
                        {
                            "cityName": "温岭市3",
                            "description": "BYSocket 的家在温岭。",
                            "id": 1,
                            "provinceId": 1
                        }
                    ],
                    {
                        "keyProperties": [
                            "id"
                        ],
                        "isUseGeneratedKeys": false
                    }
                ]
            },
            {
                "index": 2,
                "traceId": "192168015073156871042791710006ed",
                "repeatId": "192168015073156871042776710028ed",
                "success": true,
                "skip": false,
                "cost": 2,
                "originUri": "java://redis.clients.jedis.Connection/isConnected~()Z",
                "currentUri": "java://redis.clients.jedis.Connection/isConnected~()Z",
                "originArgs": [],
                "currentArgs": []
            }
        ]
    },
    "message": "operate success"
}

回放成功判定

回放结果中有一个diff字段,但是官方没有实现,所以使用官方开源版本需要人肉对比。

判断要素可参考以下两点:

  1. 回放结果字段中responseoriginResponse一致,说明本次回放的接口中,返回的接口和录制时返回的结果一致。
  2. 且 mockInvocations 中的每一个对象的success都为 true,说明执行的每一个 mock 步骤都执行成功。

PS:diff 功能可以自行在 repeater-console 实现,主要是字符串对比输出差异。

回放失败问题定位

如果发现不满足上述成功条件可以从三个方向协助定位。

  1. 查看 mockInvications 中的 mockInviocation 是否都执行成功 (success是否为true),如果出现了successfalse的情况,说明是该步骤 mock 失败导致的失败,可以进一步看看这个 mockInvication 中的currentArgsoriginArgs是否不一致,如果发现不一致,则需要检查代码逻辑,了解入参如何生成,为啥会不一致。
  2. 如果 mockInvications 中的 mockInviocation 都执行成功,reponse 和 originResponse 却不一致,可能是因为代码处理逻辑发生了调整,或者执行回放过程中发生了报错。此时先看自己的代码逻辑在最后一个 mockInvocation 到返回结果之间的代码逻辑是否有改动导致结果不一致。另外可以借助日志 repeater 的日志进一步定位,日志地址:~/logs/sandbox/repeater/repeater.log,优先看 error 类的日志内容。如果 info 级别不足够定位,可以调整至 debug 级别进行定位。
  3. 日志也没出错,逻辑也没问题,但是就是回放结果不一致,此时可以看看 mockInvocations 的数量是否与录制记录的 subInvocations 数量一致。录制记录的 subInvocations 的查看方法见本文 4.2 章节。

4.3 使用过程的调试技巧

在这里总结一下我是用过的 repeater 的调试技巧,以便后续遇到了问题可以更好地进行定位排查以及解决。

4.3.1 日志

添加日志的技巧

在 repeater 的调研试用过程之初,我对 repeater 一窍不通。过程中由于配置出错、使用姿势不对等原因,经常遇到 repeater 没有按照我预期运行的情况。

那时我还不是很懂调试手段,主要是通过看日志、看源码,在源码报错行的上下文加日志来辅助我定位问题。

添加日志的技巧有四:

  1. 在 repeater-plugins、repeater-plugins-core 两个包中的代码,都可以直接使用 repeater-plugins-core 提供的 LogUtils 来进行日志打印,而不需要重新在类中初始化 logger。
  2. 当打印的日志内容涉及一些对象时,可以使用JSONObject.toJSONString(object, SerializerFeature.IgnoreNonFieldGetter)来进行打印,这样可以以 json 格式输出对象中的详细信息,以及通过SerializerFeature.IgnoreNonFieldGetter参数规避一下序列化失败的情况。
  3. 添加日志之后,需要重新构建 repeater 并重新挂载 repeater 才能生效,为了提高效率,建议添加日志时尽量往上下文拓展,尽量一次补全流程中可能需要了解信息的日志。
  4. 调试信息请将日志等级改为 debug;而在原文中看到有部分 debug 日志比较有用时,可以调整成 info 级别的。

切记:添加日志之后,本地环境需要执行install-local.sh脚本,对 repeater 重新打包并安装到本地环境才可生效,如何重新打包,可以看1 安装与启动中的 1.3 章节。

日志定位问题的技巧

在录制回放过程中常遇到的情况以及对应日志的情况,在这里做一个小结。

以下日志没有特殊说明就是指~/logs/sandbox/repeater/repeater.log。

请求后没有生成录制记录

  1. 录制回放配置错误,请求不在录制范围内。debug 模式下,会打印日志:event missing sample rule
  2. 调用事件处理出错,:"[Error-0000]-uncaught exception occurred when dispatch event。一般可能会是序列化的错误,可以留意这个日志之后的堆栈信息是否有SerializeException相关字样的信息。
  3. 请求 repeater-console 保存记录失败:[Error-0000]-broadcast record failed
  4. repeater.log 没有异常日志,此时可考虑查看是否是 repeater-console 保存报错导致。建议检查 repeater-console 的保存逻辑。

回放结果不一致

  1. mock 步骤执行失败:can't find any sub invocation——回放过程中没有在录制记录的子调用中找到这个该 mock 请求相同的调用,需要查看这一个 mock 请求在回放应用中的调用关系,了解为何录制是没有录制到这一个子调用。
  2. mock 步骤执行失败:find invocation by xxx,but similarity not match——回放过程中在录制记录的子调用中只找到了请求相同的但没有找到参数相同的调用。需要查看不一致的参数有哪些,在回放应用的源码中找到参数生成逻辑,了解为何录制和回放为何对于同一个步骤生成了不一样的参数。
  3. 任何出现serialize相关的 error 日志都是序列化问题。

4.3.2 断点调试

在开发过程中主要使用的调试手段是以 jar 包的方式启动应用,添加 java 远程调试相关参数,并以 agent 模式启动 repeater。

命令可参考 jvm-sandbox-repeater 代码中的bin/bootstrap.sh中的启动命令。

${JAVA_HOME}/bin/java -Xdebug -Xrunjdwp:transport=dt_socket,server=y,suspend=n,address=8000 \
     -javaagent:${HOME}/sandbox/lib/sandbox-agent.jar=server.port=8820\;server.ip=0.0.0.0 \
     -Dapp.name=repeater \
     -Dapp.env=daily \
     -jar 应用.jar

其中:

  • address 字段为调试端口。
  • suspend 字段可选ynn代表启动应用过程不中断,运行过程等待远程调试器连接,而y代表调试器就绪后启动暂停,直到有远程调试器连接才继续执行启动。当我们需要调试 repeater 的初始化过程时,就需要用到y模式。

执行启动命令之后,需要调试 repeater,则使用 idea 打开 jvm-sandbox-repeater 源码,配置对应端口的远程调试,启动调试,连接上后在控制台打印日志:

Connected to the target VM, address: 'localhost:8000', transport: 'socket'

说明成功连上,可以对 repeater 进行断点调试了。

发现社区也有一篇关于 repeater 的调试说明且图文并茂的文章,这里放下传送门

共收到 5 条回复 时间 点赞

什么是 standalone 模式?

wangyijie 回复

单机模式,不需要配套的 repeater-console 即可进行录制和回放。

mockInvications 字段中数据为 null
这个是怎么回事 全部回放失败

回放的完成后 response 字段的数据是 Invoke failed, status code is not 200 这个是为什么

有 mock Java 方法的示例吗

需要 登录 後方可回應,如果你還沒有帳號按這裡 注册