快速安装

  • 下载并安装

下载最新版本的JVM-SANDBOX

  1. wget http://ompc.oss-cn-hangzhou.aliyuncs.com/jvm-sandbox/release/sandbox-stable-bin.zip

解压

  1. unzip sandbox-stable-bin.zip
  • 挂载目标应用

进入沙箱执行脚本

  1. cd sandbox/bin

目标JVM进程33342

  1. ./sandbox.sh -p 33342
  • 挂载成功后会提示
  1. ./sandbox.sh -p 33342
  2. NAMESPACE : default
  3. VERSION : 1.2.0
  4. MODE : ATTACH
  5. SERVER_ADDR : 0.0.0.0
  6. SERVER_PORT : 55756
  7. UNSAFE_SUPPORT : ENABLE
  8. SANDBOX_HOME : /Users/vlinux/opt/sandbox
  9. SYSTEM_MODULE_LIB : /Users/vlinux/opt/sandbox/module
  10. USER_MODULE_LIB : ~/.sandbox-module;
  11. SYSTEM_PROVIDER_LIB : /Users/vlinux/opt/sandbox/provider
  12. EVENT_POOL_SUPPORT : DISABLE
  • 卸载沙箱
  1. ./sandbox.sh -p 33342 -S
  2. jvm-sandbox[default] shutdown finished.
  • 其中,可以用下面命令获取当前运行Java程序的pid(替换33342)
  1. jps -l

实例

线上发生文件上传故障,异常错误提示文件存储路径异常,目的是通过jvm-sandbox在不破坏不重启的前提下获知参数中的文件存储路径是否与配置文件一致
先上代码

  1. package com.cn;
  2. import com.alibaba.jvm.sandbox.api.Information;
  3. import com.alibaba.jvm.sandbox.api.Module;
  4. import com.alibaba.jvm.sandbox.api.annotation.Command;
  5. import com.alibaba.jvm.sandbox.api.listener.ext.Advice;
  6. import com.alibaba.jvm.sandbox.api.listener.ext.AdviceListener;
  7. import com.alibaba.jvm.sandbox.api.listener.ext.EventWatchBuilder;
  8. import com.alibaba.jvm.sandbox.api.resource.ModuleEventWatcher;
  9. import org.kohsuke.MetaInfServices;
  10. import javax.annotation.Resource;
  11. import java.lang.reflect.Field;
  12. import java.util.logging.Logger;
  13. @MetaInfServices(Module.class)
  14. @Information(id = "my-sandbox-module")// 模块名,在指定挂载进程后通过-d指定模块,配合@Command注解来唯一确定方法
  15. public class MySandBoxModule implements Module {
  16. //日志输出,默认采用logback,这里的日志输出到切入的服务日志中
  17. private Logger LOG = Logger.getLogger(MySandBoxModule.class.getName());
  18. @Resource
  19. private ModuleEventWatcher moduleEventWatcher;
  20. @Command("addLog")// 模块命令名
  21. public void addLog() {
  22. new EventWatchBuilder(moduleEventWatcher)
  23. .onClass("cn.com.service.impl.PackageServiceImpl")// 想要对 PackageServiceImpl 这个类进行切面
  24. .onBehavior("bathSave")// 想要对上面类的 bathSave 方法进行切面
  25. .onWatch(new AdviceListener() {
  26. //对方法执行之前执行
  27. @Override
  28. protected void before(Advice advice) throws Throwable {
  29. //获取方法的所有参数
  30. Object[] parameterArray = advice.getParameterArray();
  31. if (parameterArray != null) {
  32. for (Object po : parameterArray) {
  33. //方法参数可能为空,规避报错
  34. if (po != null) {
  35. /**
  36. * 目标方法
  37. * public List<UploadResult> bathSave(List<Package> pkgs, MultipartFile[] files, String hdfsDir) {}
  38. *
  39. * 这里只关心MultipartFile参数,其余参数过滤
  40. *
  41. * po.getClass() 输出内容为 class [Lorg.springframework.web.multipart.MultipartFile;
  42. * po.getClass().getName() 输出内容为[Lorg.springframework.web.multipart.MultipartFile
  43. *
  44. * 最开始的设想是直接cast转型,但是由于类加载器不同,是不行的,所以最好反射来操作
  45. */
  46. if (po.getClass().getName().contains("MultipartFile")) {
  47. //目标方法参数是一个MultipartFile[]
  48. Object[] o2 = (Object[]) po;
  49. for (Object o3 : o2) {
  50. Field ff = o3.getClass().getDeclaredField("part");
  51. ff.setAccessible(true);
  52. Object part = ff.get(o3);
  53. Field ff1 = part.getClass().getDeclaredField("location");
  54. ff1.setAccessible(true);
  55. Object file = ff1.get(part);
  56. Field ff11 = file.getClass().getDeclaredField("path");
  57. ff11.setAccessible(true);
  58. //最后的结果输出:打印的路径-------/app/upload,符合预期
  59. LOG.info("打印的路径-------" + ff11.get(file));
  60. }
  61. }
  62. }
  63. }
  64. }
  65. }
  66. });
  67. }
  68. }

部署

打包

  1. mvn clean package

上传或者复制到目录

  1. /sandbox/sandbox-module

效果如下

  1. /sandbox/sandbox-module/my-sandbox-module-1.0-SNAPSHOT-jar-with-dependencies.jar

挂载到对应进程

  1. # my-sandbox-module就是类上的模块名,addLog方法上的模块命令名
  2. ./sandbox.sh -p 21815 -d 'my-sandbox-module/addLog'

挂载之后可以通过指令查看是否挂载成功

  1. # ./sandbox.sh -p 4432 -l
  2. my-sandbox-module ACTIVE LOADED 1 1 UNKNOW_VERSION UNKNOW_AUTHOR
  3. sandbox-info ACTIVE LOADED 0 0 0.0.4 luanjia@taobao.com
  4. broken-clock-tinker ACTIVE LOADED 0 0 UNKNOW_VERSION UNKNOW_AUTHOR
  5. sandbox-module-mgr ACTIVE LOADED 0 0 0.0.2 luanjia@taobao.com
  6. sandbox-control ACTIVE LOADED 0 0 0.0.3 luanjia@taobao.com
  7. total=5

这时执行对应的api在挂载的服务日志中就能查看通过沙箱添加的日志了。

sandbox的日志路径可以在/sandbox/cfg/sandbox-logback.xml文件中进行配置(sandbox日志主要输出挂载信息,以及沙箱程序的异常错误信息)

  1. <?xml version="1.0" encoding="UTF-8" ?>
  2. <configuration scan="true" scanPeriod="10000">
  3. <appender name="SANDBOX-FILE-APPENDER" class="ch.qos.logback.core.rolling.RollingFileAppender">
  4. <file>${user.home}/logs/sandbox/sandbox.log</file>
  5. <rollingPolicy class="ch.qos.logback.core.rolling.TimeBasedRollingPolicy">
  6. <FileNamePattern>${user.home}/logs/sandbox/sandbox.log.%d{yyyy-MM-dd}</FileNamePattern>
  7. <MaxHistory>30</MaxHistory>
  8. </rollingPolicy>
  9. <encoder>
  10. <pattern>%d{yyyy-MM-dd HH:mm:ss} %SANDBOX_NAMESPACE %-5level %msg%n</pattern>
  11. <charset>UTF-8</charset>
  12. </encoder>
  13. </appender>
  14. <root level="info">
  15. <appender-ref ref="SANDBOX-FILE-APPENDER"/>
  16. </root>
  17. </configuration>

以上就是对Alibaba的jvm-sandbox的初体验,其他更丰富的场景还有待进一步体验

参考文档

  1. https://www.infoq.cn/article/TSY4lGjvSfwEuXEBW*Gp
  2. https://github.com/alibaba/jvm-sandbox
  3. https://blog.csdn.net/m0_38115840/article/details/99453672