一、前言
在开始之前,我们先来模拟一下以下的场景:
小李:“小明,你的接口没有返回数据,麻烦帮忙看一下?”
小明:“我这边的数据也是从别人的服务器中拿到的,但是我不确定是因为逻辑处理有问题导致没有结果,还是因为我依赖的服务有问题而没有返回结果,我需要确认一下。”
小明:“哎呀,线上没有日志,我需要加个日志上个线。”
30 分钟之后……
小明:“不好意思,日志加错地方了……稍等……”
接来下隆重登场的就是本文的主角 JVM SandBox 了。基于 JVM SandBox,我们可以很容易地做到在不重新部署应用的情况下,给指定的某些类的某些方法加上日志功能。当然,动态加日志仅仅是 JVM SandBox 可以应用的一个小小的场景,JVM SandBox 的威力远不在于此。那么,JVM SandBox 是什么?JVM SandBox 从哪里来?JVM SandBox 怎么用?本文在第二章会回答这几个问题,如果你跟我一样对 JVM SandBox 的底层实现原理感兴趣,特别是 JVM 相关部分,那么第三章有相关的内容;如果你只想了解 JVM SandBox 自身具有哪些特性,以及 JVM SandBox 是如何设计实现的,那么可以跳过第三章,直接阅读第四章;最后,在第五章会简单地介绍其他两个可以应用 JVM SandBox 的场景。
二、JVM SandBox 简介
2.1 AOP
在介绍 JVM SandBox 之前,我们先来回顾一下 AOP 技术。
AOP(面向切面编程,Aspect Oriented Programming)技术已被业界广泛应用,其思想是面向业务处理过程的某个步骤或阶段进行编程,这个步骤或阶段被称为切面,其目的是降低业务逻辑的各部分之间的耦合,常见的 AOP 实现基本原理有两种:代理和行为注入。
1)代理模式
在代理模式下,我们会创建一个代理对象来代理原对象的行为,代理对象拥有原对象行为执行的控制权,在这种模式下,我们基于代理对象在原对象行为执行的前后插入代码来实现 AOP。
图 2-1 代理模式
2)行为注入模式
在行为注入模式下,我们不会创建一个新的对象,而是修改原对象,在原对象行为的执行前后注入代码来实现 AOP。
图 2-2 行为注入模式
2.2 JVM SandBox
JVM SandBox 是阿里开源的一款 JVM 平台非侵入式运行期 AOP 解决方案,本质上是一种 AOP 落地形式。那么可能有同学会问:已有成熟的 Spring AOP 解决方案,阿里巴巴为什么还要“重复造轮子”?这个问题要回到 JVM SandBox 诞生的背景中来回答。在 2016 年中,天猫双十一催动了阿里巴巴内部大量业务系统的改动,恰逢徐冬晨(阿里巴巴测试开发专家)所在的团队调整,测试资源保障严重不足,迫使他们必须考虑更精准、更便捷的老业务测试回归验证方案。开发团队面临的是新接手的老系统,老的业务代码架构难以满足可测性的要求,很多现有测试框架也无法应用到老的业务系统架构中,于是需要新的测试思路和测试框架。
三.使用
3.1下载
安装jvm-sandBox
# 下载最新版本的JVM-SANDBOX
wget http://ompc.oss-cn-hangzhou.aliyuncs.com/jvm-sandbox/release/sandbox-stable-bin.zip
# 解压
unzip sandbox-stable-bin.zip
3.2安装
# 进入sandbox执行脚本
cd sandbox/bin
# 项目进程id 55731(使用jps命令查看或者ps命令)
./sandbox.sh -p 55714
rong:bin rss$ ./sandbox.sh -p 55714
NAMESPACE : default
VERSION : 1.3.3
MODE : ATTACH
SERVER_ADDR : 0.0.0.0
SERVER_PORT : 54584
UNSAFE_SUPPORT : ENABLE
SANDBOX_HOME : /Users/rss/jvm-sendbox/sandbox/bin/..
SYSTEM_MODULE_LIB : /Users/rss/jvm-sendbox/sandbox/bin/../module
USER_MODULE_LIB : /Users/rss/jvm-sendbox/sandbox/sandbox-module;~/.sandbox-module;
SYSTEM_PROVIDER_LIB : /Users/rss/jvm-sendbox/sandbox/bin/../provider
EVENT_POOL_SUPPORT : DISABLE
出现以上信息,表示sandbox 启动成功
3.3使用
将切面项目打包到sandbox/sand-module目录下,如下图
切面项目代码:
监控目标项目
类:com.rss.gateway.controller.Demo
方法:sandBoxDemo
import com.alibaba.jvm.sandbox.api.Information;
import com.alibaba.jvm.sandbox.api.Module;
import com.alibaba.jvm.sandbox.api.annotation.Command;
import com.alibaba.jvm.sandbox.api.listener.ext.Advice;
import com.alibaba.jvm.sandbox.api.listener.ext.AdviceListener;
import com.alibaba.jvm.sandbox.api.listener.ext.EventWatchBuilder;
import com.alibaba.jvm.sandbox.api.resource.ModuleEventWatcher;
import lombok.extern.slf4j.Slf4j;
import org.kohsuke.MetaInfServices;
import javax.annotation.Resource;
@MetaInfServices(Module.class)
@Information(id = "gateway")// 模块名,在指定挂载进程后通过-d指定模块=
@Slf4j
public class MySandBoxModule implements Module {
@Resource
private ModuleEventWatcher moduleEventWatcher;
@Command("checkDemo")
public void checkDemo() {
new EventWatchBuilder(moduleEventWatcher)
//切入目标类
.onClass("com.rss.gateway.controller.SandBoxDemo")
//切入目标方法
.onBehavior("sandBoxDemo")
.onWatch(new AdviceListener() {
//对方法执行之前执行
@Override
protected void before(Advice advice) {
//在这里可以做很多事情,demo只打印了日志信息
System.out.println("sandbox切入");
//获取方法的所有参数
Object[] parameters = advice.getParameterArray();
if (parameters != null) {
for (Object obj : parameters) {
System.out.println("参数类型:" + obj.getClass().getName() + "!!!!!!!");
System.out.println("参数值:" + obj + "!!!!!!!");
}
}
}
});
}
}
执行 使用自定义module执行
./sandbox.sh -p 55714 -d 'gateway/checkDemo'
目标项目中的方法如下代码:
@RestController
@Slf4j
public class SandBoxDemo {
@RequestMapping(value = "/sandBoxDemo")
public void sandBoxDemo(HttpServletRequest request, String data)throws Exception{
log.info("我是被切入的目标方法" + data);
}
}
访问:http://localhost:20977/sandBoxDemo?data=111111
结果如下:
17:10:10.572 [http-nio-20977-exec-2] INFO com.online.test.MySandBoxModule - sandbox切入成功!!!!!!
17:10:10.572 [http-nio-20977-exec-2] INFO com.online.test.MySandBoxModule - 参数类型为:org.springframework.web.multipart.support.StandardMultipartHttpServletRequest!!!!!!!
17:10:10.572 [http-nio-20977-exec-2] INFO com.online.test.MySandBoxModule - 参数值为:org.springframework.web.multipart.support.StandardMultipartHttpServletRequest@499a1054!!!!!!!
17:10:10.572 [http-nio-20977-exec-2] INFO com.online.test.MySandBoxModule - 参数类型为:java.lang.String!!!!!!!
17:10:10.572 [http-nio-20977-exec-2] INFO com.online.test.MySandBoxModule - 参数值为:111111!!!!!!!
17:10:10.572 [http-nio-20977-exec-2] INFO com.online.test.MySandBoxModule - 我是被切入的目标方法111111
3.4退出
直接停止目标(被切入的项目)项目,不建议用这种方式
用命令退出
./sandbox.sh -p 55714 -S
四.其他
其他常用命令
#卸载沙箱
./sandbox.sh -p 55714 -S
#查询沙箱
./sandbox.sh -p 55714 -l
#刷新沙箱
./sandbox.sh -p 55714 -F
#使用自定义module执行
./sandbox.sh -p 55714 -d 'gateway/checkDemo'
#日志配置及查看
#配置文件在 /sandbox/rss/sandbox-logback.xml
#默认日志路径 ~/logs/sandbox/sandbox.log
五.讨论话题:
怎样更优雅的监控项目:
1.实时监控项目PID
2.获取方法的更新
3.热启动
参考文档
1.https://www.infoq.cn/article/TSY4lGjvSfwEuXEBW*Gp
2.https://github.com/alibaba/jvm-sandbox