了解 JVM-Sandbox 和 JVM-Sandbox-Repeater

JVM-Sandbox 提供字节码插桩功能,用来改变目标程序运行时的代码,通过插入字节码增强程序中的功能; JVM-Sandbox-Repeater 是基于 JVM-Sandbox 的字节码插桩实现了流量录制和回放功能。

准备 JVM-Sandbox-Repeater 相关环境

1、git clone 下源码 https://github.com/alibaba/jvm-sandbox-repeater.git

2、repeater 数据库准备

录制的数据需要进行保存,所以需要数据库,同时官网提供了前端页面展示。

查看 repeater-console-start 项目下的 application.properties

image-20220215140254013

可以知道需要新建数据库 repeater, 数据库表结构文件在 repeater-console-dal 中的资源目录下

image-20220215140520656

3、查看程序的启动方式

程序的运行方法包含在三个文件中分别是:bootstrap.sh, install-local.sh , package.sh

Repeater 启动命令
${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 ${HOME}/.sandbox-module/repeater-bootstrap.jar
> ${JAVA_HOME}/bin/java
>
> 指定Java程序所在位置



> -Xdebug -Xrunjdwp:transport=dt_socket,server=y,suspend=n,address=8000
>
> 远程Debug 的设置,如不用可以省略



> -javaagent:${HOME}/sandbox/lib/sandbox-agent.jar=server.port=8820\;server.ip=0.0.0.0 \
>
> 以 permain 的方式指定代理,启动时进行插桩,具体详见 instanceation , jvm 提供了两种入口方式分别是 premain 和 agentmain



> -Dapp.name=repeater \
> -Dapp.env=daily \
>
> 这是传递的两个参数



> -jar ${HOME}/.sandbox-module/repeater-bootstrap.jar
>
> 启动 Java 程序
Console 存储录制的 HTTP 响应中文乱码问题

录制应用请求时响应中的中文存储乱码

经查询响应数据流转,已经确定问题出现在官方提供的插件 http-plugin 代码中

image-20211104164654228

image-20211104165429008

如果没有设置字符编码,默认会使用 ISO-8859-1 然而这个字符编码是不支持中文的,所以会出现乱码,setResponse 的时候指定字符编码或者 wtm.copier.setCharacterEncoding("UTF-8"); 或者使用其他方式,参考类 API 文档即可。

关于设置白名单列表产生的问题

当想设置抓取全部流量的时候就把他放开了,没想到出了问题。

config.setHttpEntrancePatterns(Lists.newArrayList("([\\s\\S]*)"));

reapater 当抓取到请求之后,就会调用自己本身的保存接口 http://127.0.0.1:8001/facade/api/record/save 将记录保存起来,因为录制时将白名单放开了,所以就会不断的录制自己的 http://127.0.0.1:8001/facade/api/record/save , 接口,又因为实际上他将每次的请求体录制下来的同时进行了序列化(序列化后请求体变大)交给 save 接口保存 , 导致请求体越来越大,而且增长速度飞快。很快就会造成数据库表很大,JAVA 堆栈溢出等问题。

界面中的时间显示

用 repeater 前端接口查看记录时,会发现显示的时间有点特殊,我们正常的时间显示是 2022-01-01 12:01:12 , 但是在前端界面显示的时候会显示为 2022-01-01 12:01:12.0 , 经过查看是 java.sql.TimestamptoString() 就是这么写的,最后的 0 表示纳秒。

image-20220215152553256

想按 2022-01-01 12:01:12 这种形式展示,只需要重新定义一个自己的 Date 类,重写 toString() 方法,在通过反射的方式将时间转换为自己自定义的对象即可。

public class BeantimeUtil {

    private static String pattern = "yyyy-MM-dd HH:mm:ss";

    public static  <T> void Restore(T t) {
        Class clazz = t.getClass();
        handleRestore(clazz, t);
    }

    public static <T> void handleRestore(Class clazz, T t){

        Field[] fields = clazz.getDeclaredFields();

        for (Field field : fields) {
            field.setAccessible(true);
            if (field.getAnnotatedType().getType().getTypeName().equals(Date.class.getName())) {
                SimpleDateFormat dateFormat = new SimpleDateFormat(pattern);
                try {
                    if (Optional.ofNullable(field.get(t)).isPresent()) {
                        String date = dateFormat.format(field.get(t));
                        Date date_obj = dateFormat.parse(date, new ParsePosition(0));
                        field.set(t, new CustomDate(date_obj.getTime()));
                    }
                } catch (IllegalAccessException e) {
                    // lgnore
                    e.printStackTrace();
                }
            }
        }

        Class superclass = clazz.getSuperclass();

        if (superclass.getName().equals("java.lang.Object") || Modifier.isAbstract(superclass.getModifiers())){
            return;
        }

        handleRestore(superclass, t);
    }

}
public class CustomDate extends Date {

    public CustomDate(long time) {
        this.time = time;
    }

    private long time;

    @Override
    public String toString() {
        long date = time;
        SimpleDateFormat dateFormat = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
        return dateFormat.format(new Date(date));
    }
}


↙↙↙阅读原文可查看相关链接,并与作者交流