实现JVM-Sandbox-Repeater与Module间的数据共享

一、背景

我们的项目同时使用了 JVM-Sandbox-Repeater(用于流量录制回放)和自定义 JVM-Sandbox 模块(用于特定监控场景)。

由于 Repeater 和 Sandbox Module 遵循沙箱的模块隔离原则,它们运行在独立的类加载器中,无法直接共享数据。这导致了一些关键信息(如traceId)无法在两个组件间传递,限制了更复杂的监控场景实现。

本文将探讨如何突破这一限制,实现 JVM-Sandbox-Repeater 与 Sandbox Module 间的数据共享。

二、理解JVM-Sandbox Module

2.1 模块隔离的特性与挑战

JVM-Sandbox Module 是 JVM-Sandbox(沙箱)容器中实现具体功能逻辑的插件化单元。它利用 JVM-Sandbox 提供的动态字节码增强能力,让你能够以无侵入的方式,在目标 JVM 应用的运行期间,对其特定类的特定方法进行 AOP 拦截和增强。

关键特性:

  • 无侵入性:无需修改源码或重启服务
  • 动态插拔:模块可实时加载、卸载
  • 类隔离:模块间、模块与应用间类加载器隔离

隔离带来的挑战:

  • 模块A无法直接访问模块B的类
  • 模块无法直接访问业务应用中的自定义类
  • 默认情况下,模块间无法共享数据

2.2 模块应用场景

可以把 JVM-Sandbox Module 想象成一个功能强大的"监听器"或"拦截器"。它通过 JVM-Sandbox 容器"附着"到目标 JVM 进程上,监听你感兴趣的方法调用事件(例如方法执行前、返回后、抛出异常时),并在这些事件点执行你自定义的逻辑,比如记录日志、修改参数、模拟异常、统计耗时等。

  • 线上故障定位与诊断:类似 Arthas 的功能,例如动态跟踪方法调用参数、返回值、异常信息,排查性能瓶颈等。
  • 流量录制与回放(Repeater):录制线上用户的真实请求(入参和出参),用于线下回放测试、故障复现和诊断。阿里开源的 JVM-sandbox-repeater 正是基于此实现的。
  • 故障模拟与演练:模拟各种异常场景,如方法超时、抛出特定异常、返回错误结果等,以检验系统的容错性和稳定性。ChaosBlade 的 Java 场景实现就基于 JVM-Sandbox。官方自带的无敌破坏王插件:DebugRalphModule

三、数据共享方案

3.1 难点

  • 每个 Module 使用独立的 ModuleJarClassLoader → 类彼此隔离(即模块 A 无法直接访问模块 B 的类)。
  • 模块默认无法直接访问目标应用中的业务自定义类(如:com.xx.UserService),只能通过沙箱 API 提供的 Advice 对象,利用反射机制获取这些类的信息(如:类名、方法名、参数等)。

3.2 核心思路:全局共享

本次实现的目标是在 Repeater回放过程中,将 traceId 共享给 Sandbox Module 使用。

解决方案的核心是:通过 Bootstrap ClassLoader 加载共享类,创建全局数据,双方通过反射机制访问该类。

3.3 具体步骤

1、定义共享类

通过 mvn package 打包生成 JAR 文件,并将其放置于 JVM-sandbox 目录下

typescript 复制代码
public class ShareData {
​
    public static BlockingQueue<String> shareQueue = new LinkedBlockingQueue<>();
​
    public static void put(String data) {
        shareQueue.add(data);
    }
​
    public static void remove(String data) {
        shareQueue.remove(data);
    }
​
    public static void removeAll() {
        shareQueue.remove();
    }
​
    public static String get() throws InterruptedException {
        return shareQueue.take();
    }
}

2、全局加载共享类

修改 AgentLauncher 启动逻辑,确保共享类被 BootClassLoader 加载器加载,使其在全局范围内可访问

java 复制代码
com.alibaba.jvm.sandbox.agent.AgentLauncher@install
​
// 将Jar包注入到BootstrapClassLoader
inst.appendToBootstrapClassLoaderSearch(new JarFile(new File(
         "/opt/jvm-sandbox/sandbox/lib/ShareData.jar"
)));

3、Repeater 端写入数据

在回放过程中,通过反射将 traceId 写入共享区

typescript 复制代码
cn.zcygov.jvm.sandbox.repeater.plugin.core.impl.AbstractRepeater@repeat
​
static volatile Method putTraceMethod;
​
// 
public String putTrace(String traceId) {
        if (putTraceMethod == null) {
            synchronized (LOCK) {
                if (putTraceMethod == null) {
                    try {
                        // 反射调用
                        putTraceMethod = Class.forName("zcy.ShareData", false, null).getDeclaredMethod("put", String.class);
                        putTraceMethod.setAccessible(true);
                    } catch (Exception e) {
                        throw new RuntimeException("Failed to initialize traceMethod", e);
                    }
                }
            }
        }
​
        try {
            // 存储
            return (String) putTraceMethod.invoke(null,traceId);
        } catch (Exception e) {
            throw new RuntimeException("Trace invocation failed", e);
        }
}

4、Sandbox Module 端读取数据

在自定义模块中,通过反射从共享区读取数据

java 复制代码
com.alibaba.jvm.sandbox.module.debug.TestModule
​
static volatile Method getTraceMethod;
​
public String getTrace() {
        if (getTraceMethod == null) {
            synchronized (LOCK) {
                if (getTraceMethod == null) {
                    try {
                        getTraceMethod = Class.forName("zcy.ShareData", false, null).getDeclaredMethod("get");
                        getTraceMethod.setAccessible(true);
                    } catch (Exception e) {
                        throw new RuntimeException("Failed to initialize traceMethod", e);
                    }
                }
            }
        }
​
        try {
            return (String) getTraceMethod.invoke(null);
        } catch (Exception e) {
            throw new RuntimeException("Trace invocation failed", e);
        }
}

四、尾声

本文介绍了 基于共享类 + BootClassLoader 注入的数据共享机制,解决了 JVM-Sandbox 与 Repeater 因类加载器隔离导致的数据共享问题。 未来可在此机制上扩展更多业务场景,如定制化质量工具、统一调用链追踪等。

相关推荐
文心快码BaiduComate5 分钟前
文心快码入选2025服贸会“数智影响力”先锋案例
前端·后端·程序员
neoooo10 分钟前
🌐 Cloudflare Tunnel vs ZeroTier:两个世界的内网穿透哲学
后端
涡能增压发动积14 分钟前
当你不了解“异步”时请慎用“异步”——记一次生产环境故障排查之旅
后端
文心快码BaiduComate19 分钟前
用Comate Zulu开发一款微信小程序
前端·后端·微信小程序
用户83562907805121 分钟前
Python 删除 Excel 工作表中的空白行列
后端·python
Json_22 分钟前
使用python-fastApi框架开发一个学校宿舍管理系统-前后端分离项目
后端·python·fastapi
ytadpole22 分钟前
Java 25 新特性 更简洁、更高效、更现代
java·后端
风一样的树懒1 小时前
死信队列:你在正确使用么?
后端
RoyLin1 小时前
TypeScript设计模式:门面模式
前端·后端·typescript
JavaGuide2 小时前
JDK 25(长期支持版) 发布,新特性解读!
java·后端