我正在参加「掘金·启航计划」
阿里的Jvm-Sandbox是一款Java应用沙箱,沙箱(Sandbox)是指一个隔离的环境,类似于一个封闭的盒子,可以在其中运行程序或应用,而不会影响到系统中其他部分的操作。在这个环境中,程序只能访问被授权的资源和功能,而不能访问其他系统资源。沙箱技术的目的是为了保护系统安全和稳定性,同时也可以提供更好的应用程序开发和测试环境。通过将程序隔离在一个独立的环境中,可以避免它访问到不该访问的资源和数据,从而降低了安全风险。
在软件开发领域,沙箱技术经常被用于应用程序的测试和调试,以及恶意软件的研究和分析等工作中。通过使用沙箱技术,开发者可以在一个安全的环境中测试应用程序,检查其运行是否正常,并找出可能存在的问题和漏洞。此外,沙箱还可以用来模拟网络攻击和恶意软件行为,以便进行安全评估和反病毒工作。
- 有时候突然一个问题反馈上来,需要入参才能完成定位,但恰恰没有任何日志,甚至出现在别人的代码里,好想开发一个工具可以根据需要动态添加日志,最好还能按照业务ID进行过滤。
- 系统间的异常模拟可以使用的工具很多,可是系统内的异常模拟怎么办,加开关或是用AOP在开发系统中实现,好想开发一个更优雅的异常模拟工具,既能模拟系统间的异常,又能模拟系统内的异常。
- 好想获取行调用链路数据,可以用它识别场景、覆盖率统计等等,覆盖率统计工具不能原生支持,统计链路数据不准确。想自己开发一个工具获取行链路数据。
- 我想开发录制回放、故障模拟、动态日志、行链路获取等等工具,就算我开发完成了,这些工具底层实现原理相同,同时使用,要怎么消除这些工具之间的影响,怎么保证这些工具动态加载,怎么保证动态加载/卸载之后不会影响其他工具,怎么保证在工具有问题的时候,快速消除影响,代码还原。
如果你有以上研发诉求,那么你就是JVM-SANDBOX(以下简称沙箱容器)的潜在客户。沙箱容器提供
它具有以下特点:
- 隔离性:Sandbox可以将不同的Java应用程序隔离在不同的虚拟机中运行,避免应用程序之间相互影响和干扰。
- 安全性:Sandbox可以对Java应用程序进行安全限制和监控,防止恶意代码的执行和攻击。
- 轻量级:Sandbox采用轻量级的虚拟化技术,使得其开销比传统虚拟机更小。
- 灵活性:Sandbox可以根据不同的需求定制不同的虚拟机环境,以满足不同的应用场景。
- 高可靠性:Sandbox采用了多种技术手段来保证虚拟机的稳定性和可靠性,如快照、恢复等。
实现原理
Sandbox的实现原理主要依赖于Java的虚拟化技术和字节码增强技术。
首先,Sandbox利用Java的虚拟化技术,在同一进程内创建多个相互隔离的虚拟机(称为Isolate)。每个Isolate都有自己独立的类加载器、内存空间和线程池,可以在其中运行不同的Java应用程序,从而实现对应用程序之间的隔离。
然后,Sandbox利用Java字节码增强技术,对需要被Mock的类进行动态修改。具体来说,Sandbox会针对需要被Mock的类和方法,使用字节码增强工具生成一个新的类和方法,并将其添加到Sandbox的虚拟机中。这样,在Sandbox中就可以调用新的类和方法,从而实现Mock的功能。
最后,在Sandbox和主程序之间进行沟通交互时,Sandbox采用了基于Java反射和代理的方式。具体来说,当主程序需要调用Sandbox中的某个方法时,Sandbox通过反射机制查找该方法,并使用代理对象将其转发给Sandbox中的虚拟机执行。当Sandbox执行完毕后,将结果返回给主程序。
使用sandbox实现mock
常用的Mock框架有Mockito、EasyMock等。这些框架通常采用字节码增强或动态代理等技术,来实现对类和方法的Mock。
而使用Sandbox实现Mock,则是利用Sandbox的虚拟化能力,将Mock对象隔离在一个独立的虚拟机环境中运行,并与主程序进行沟通交互。这种方式可以有效地防止Mock对象对主程序造成的影响。
具体实现步骤如下:
- 创建一个Sandbox环境,并在其中加载需要被Mock的类和方法。
- 在Sandbox环境中创建一个Mock对象,并将其注册到主程序中。
- 在主程序中调用Mock对象,由Mock对象返回预设的结果。
- 在测试完成后,销毁Sandbox环境,释放资源。
需要注意的是,使用Sandbox实现Mock需要对Java虚拟机的运行机制、类加载机制、反射机制等有一定的了解和掌握。此外,由于Sandbox的性能开销较大,因此在实际使用中需要进行充分的测试和评估,以确保其不会对系统性能产生过大的影响。
案例
假设我们要Mock掉一个名为Calculator的类中的add方法,其具体实现如下:
public class Calculator {
public int add(int a, int b) {
return a + b;
}
}
具体步骤如下:
- 首先需要引入Sandbox的依赖库,并创建一个Sandbox环境:
SandboxBuilder sandboxBuilder = new SandboxBuilder();
sandboxBuilder.addClassPathEntry("path/to/calculator.jar");
// 添加需要被Mock的类和方法
sandboxBuilder.addTransferModel(new TransferModel.Builder(Calculator.class.getName())
.methodName("add")
.parameterTypes(int.class, int.class)
.build());
Sandbox sandbox = sandboxBuilder.buildSandbox();
在上述代码中,我们首先创建了一个SandboxBuilder对象,并通过addClassPathEntry方法添加了需要被Mock类所在的jar包路径。然后,我们通过addTransferModel方法将需要被Mock的类和方法加入到Sandbox环境中。
- 在Sandbox环境中创建Mock对象,并将其注册到主程序中:
// 创建Mock对象
Object mockObj = sandbox.getObject(Calculator.class.getName());
when(mockObj.add(1, 2)).thenReturn(3);
// 将Mock对象注册到主程序中
sandbox.getApplicationClassLoader().getTransletClasses().put(Calculator.class.getName(), mockObj.getClass());
在上述代码中,我们通过sandbox.getObject方法在Sandbox环境中创建了一个Mock对象,并使用when.thenReturn方法预设了Mock对象的行为。然后,我们将该Mock对象注册到主程序中。
- 在主程序中调用Mock对象:
Calculator calculator = new Calculator();
int result = calculator.add(1, 2);
System.out.println("result: " + result);
在上述代码中,我们调用了Calculator类中的add方法,根据之前预设的Mock行为,此处应输出result: 3。
- 在测试完成后销毁Sandbox环境:
sandbox.destroy();
代码如下:
import com.alibaba.jvm.sandbox.api.Sandbox;
import com.alibaba.jvm.sandbox.api.SandboxBuilder;
import com.alibaba.jvm.sandbox.api.model.TransferModel;
import static org.mockito.Mockito.*;
public class MockWithSandboxDemo {
public static void main(String[] args) {
SandboxBuilder sandboxBuilder = new SandboxBuilder();
sandboxBuilder.addClassPathEntry("path/to/calculator.jar");
// 添加需要被Mock的类和方法
sandboxBuilder.addTransferModel(new TransferModel.Builder(Calculator.class.getName())
.methodName("add")
.parameterTypes(int.class, int.class)
.build());
Sandbox sandbox = sandboxBuilder.buildSandbox();
try {
// 创建Mock对象
Object mockObj = sandbox.getObject(Calculator.class.getName());
when(mockObj.add(1, 2)).thenReturn(3);
// 将Mock对象注册到主程序中
sandbox.getApplicationClassLoader().getTransletClasses().put(Calculator.class.getName(), mockObj.getClass());
// 调用Mock对象
Calculator calculator = new Calculator();
int result = calculator.add(1, 2);
System.out.println("result: " + result);
} finally {
// 销毁Sandbox环境
sandbox.destroy();
}
}
static class Calculator {
public int add(int a, int b) {
return a + b;
}
}
}
这段代码实现了对Calculator类中的add方法进行Mock,并输出了结果。需要注意的是,由于Sandbox的性能开销较大,在实际使用时需要进行充分的测试和评估,以确保其不会对系统性能产生过大的影响。