线上故障定位
线上系统流控
线上故障模拟
方法请求录制和结果回放
动态日志打印
安全信息监测和脱敏
JVM-SANDBOX还能帮助你做很多很多,取决于你的脑洞有多大了。
开源代码库: https://github.com/alibaba/jvm-sandbox
修复破损的时钟(gitlab 示例):
【准备阶段】
我们定义了一个钟,期望可以实现每隔一定的时间进行报时(gitlab 代码)。
package com.taobao.demo;
/**
* 报时的钟
*/
public class Clock {
// 日期格式化
private final java.text.SimpleDateFormat clockDateFormat
= new java.text.SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
/**
* 状态检查
*/
final void checkState() {
throw new IllegalStateException("STATE ERROR!");
}
/**
* 获取当前时间
*
* @return 当前时间
*/
final java.util.Date now() {
return new java.util.Date();
}
/**
* 报告时间
*
* @return 报告时间
*/
final String report() {
checkState();
return clockDateFormat.format(now());
}
/**
* 循环播报时间
*/
final void loopReport() throws InterruptedException {
while (true) {
try {
System.out.println(report());
} catch (Throwable cause) {
cause.printStackTrace();
}
Thread.sleep(1000);
}
}
public static void main(String... args) throws InterruptedException {
new Clock().loopReport();
}
}
运行代码
很明显,这个钟的实现有问题,运行起来没有正确的报时,但却一直在报异常!
问题分析
问题出在了checkState()方法上,这个方法中抛出了异常。接下来我们通过构建一个沙箱模块作为例子,修复这个损坏的钟!
解决方案
如果能直接修改checkState()方法的执行逻辑,让其不再抛异常,而是直接返回,那么一切就迎刃而解。但如何在不修改目标代码、不重启目标应用的情况下实现这个功能呢?
【修复阶段】
创建一个Java工程
1、pom 文件 引入sandbox 依赖
4.0.0
org.springframework.boot
spring-boot-starter-parent
2.5.4
com.example
box-mock
0.0.1-SNAPSHOT
box-mock
Demo project for Spring Boot
1.8
com.alibaba.jvm.sandbox
sandbox
1.3.1
pom
com.alibaba.jvm.sandbox
sandbox-api
1.3.1
provided
com.alibaba.jvm.sandbox
sandbox-common-api
1.3.1
com.alibaba.jvm.sandbox
sandbox-core
1.3.1
com.sun
tools
org.springframework.boot
spring-boot-starter
org.springframework.boot
spring-boot-starter-test
test
org.kohsuke.metainf-services
metainf-services
1.8
org.apache.maven.plugins
maven-compiler-plugin
3.1
1.8
1.8
maven-assembly-plugin
jar-with-dependencies
make-assemble
package
single
2、编写你要实现的代码逻辑
package com.alibaba.jvm.sandbox.demo;
import com.alibaba.jvm.sandbox.api.Information;
import com.alibaba.jvm.sandbox.api.Module;
import com.alibaba.jvm.sandbox.api.ProcessController;
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 org.kohsuke.MetaInfServices;
import javax.annotation.Resource;
/***
* 方式一 实现 Module
**/
@MetaInfServices(Module.class)
@Information(id = "broken-clock-tinker")
public class BrokenClockTinkerModule implements Module {
@Resource
private ModuleEventWatcher moduleEventWatcher;
@Command("repairCheckState")
public void repairCheckState() {
new EventWatchBuilder(moduleEventWatcher)
.onClass("com.taobao.demo.Clock")
.onBehavior("checkState")
.onWatch(new AdviceListener() {
/**
* 拦截{@code com.taobao.demo.Clock#checkState()}方法,当这个方法抛出异常时将会被
* AdviceListener#afterThrowing()所拦截
*/
@Override
protected void afterThrowing(Advice advice) throws Throwable {
// 在此,你可以通过ProcessController来改变原有方法的执行流程
// 这里的代码意义是:改变原方法抛出异常的行为,变更为立即返回;void返回值用null表示
ProcessController.returnImmediately(null);
}
});
}
}
###############分割线#######################
package com.example.boxmock.mock;
import com.alibaba.jvm.sandbox.api.Information;
import com.alibaba.jvm.sandbox.api.LoadCompleted;
import com.alibaba.jvm.sandbox.api.Module;
import com.alibaba.jvm.sandbox.api.ProcessController;
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 org.kohsuke.MetaInfServices;
import javax.annotation.Resource;
/**
* 方式二 实现 Module, LoadCompleted
* Created on 2022-03-13
*/
@MetaInfServices(Module.class)
@Information(id = "broken-clock-tinker")
public class BrokenClockTinkerModule implements Module, LoadCompleted {
@Resource
private ModuleEventWatcher moduleEventWatcher;
@Override
public void loadCompleted() {
new EventWatchBuilder(moduleEventWatcher)
.onClass("com.taobao.demo.Clock")
.onBehavior("checkState")
.onWatch(new AdviceListener() {
/**
* 拦截{@code com.taobao.demo.Clock#checkState()}方法,当这个方法抛出异常时将会被
* AdviceListener#afterThrowing()所拦截
*/
@Override
protected void after(Advice advice) throws Throwable {
ProcessController.returnImmediately(true);
}
});
}
}
方式一、方式二区别在于后面启动JVM-sanbox 方式不同
3、修复时钟
3.1、编译部署clock-tinker模块 运行命令完成打包
mvn clean package
3.2、下载并安装最新版本阿里SandBox包:
下载地址:https://ompc.oss.aliyuncs.com/jvm-sandbox/release/sandbox-stable-bin.zip
解压: unzip sandbox-stable-bin.zip
3.3、将修复代码打好的包复制到解压后的 sandbox/sandbox-module/目录下
cp target/****-SNAPSHOT-jar-with-dependencies.jar /sandbox/sandbox-module/
3.4 查看 报错的 Clock 代码的 进程号:
命令: jps
3.5 启动沙箱
假设目标进程号:64927
启动沙箱 进入sandbox/bin 执行启动脚本
./sandbox.sh -p 64927
NAMESPACE : default
VERSION : 1.2.0
MODE : ATTACH
SERVER_ADDR : 0.0.0.0
SERVER_PORT : 56854
UNSAFE_SUPPORT : ENABLE
SANDBOX_HOME : /Users/vlinux/opt/sandbox
SYSTEM_MODULE_LIB : /Users/vlinux/opt/sandbox/module
USER_MODULE_LIB : ~/.sandbox-module;
SYSTEM_PROVIDER_LIB : /Users/vlinux/opt/sandbox/provider
EVENT_POOL_SUPPORT : DISABLE
查看模块
./sandbox.sh -p 64229 -l
broken-clock-tinker ACTIVE LOADED 0 0 UNKNOW_VERSION UNKNOW_AUTHOR
sandbox-info ACTIVE LOADED 0 0 0.0.4 luanjia@taobao.com
sandbox-module-mgr ACTIVE LOADED 0 0 0.0.2 luanjia@taobao.com
sandbox-control ACTIVE LOADED 0 0 0.0.3 luanjia@taobao.com
total=4
可以看到broken-clock-tinker模块已经正确被沙箱所加载
如果修复代码用的是方法二 、到此为止 破损的时钟就已经被修复了,但是如果用的是方法一 修复clock#checkState()方法还需要一个触发操作
./sandbox.sh -p 64229 -d 'broken-clock-tinker/repairCheckState'
除此之外 也可以用接口形式触发 参考:https://www.cnblogs.com/moonpool/p/14510443.html
3.6 问题修复
过一会,模块生效完成,你就会发现原本一直抛异常的钟已经开始在刷新时间了,
java.lang.IllegalStateException: STATE ERROR!
at com.taobao.demo.Clock.checkState(Clock.java:16)
at com.taobao.demo.Clock.report(Clock.java:34)
at com.taobao.demo.Clock.loopReport(Clock.java:44)
at com.taobao.demo.Clock.main(Clock.java:53)
java.lang.IllegalStateException: STATE ERROR!
at com.taobao.demo.Clock.checkState(Clock.java:16)
at com.taobao.demo.Clock.report(Clock.java:34)
at com.taobao.demo.Clock.loopReport(Clock.java:44)
at com.taobao.demo.Clock.main(Clock.java:53)
java.lang.IllegalStateException: STATE ERROR!
at com.taobao.demo.Clock.checkState(Clock.java:16)
at com.taobao.demo.Clock.report(Clock.java:34)
at com.taobao.demo.Clock.loopReport(Clock.java:44)
at com.taobao.demo.Clock.main(Clock.java:53)
2018-10-23 22:31:39
2018-10-23 22:31:40
2018-10-23 22:31:41
2018-10-23 22:31:42
2018-10-23 22:31:43
2018-10-23 22:31:44
恢复被修复的check()方法
当你卸载掉JVM-SANDBOX时候,你就会发现原本已经被修复好的钟,又开始继续报错了。原因是原来通过clock-tinker模块修复的checkState()方法随着沙箱的卸载又恢复成原来错误的代码流程。
3.7、卸载沙箱
./sandbox.sh -p 64229 -S
可以通过这个工具最很多东西比如mock 网上有个很不错的mock开源项目
https://github.com/chufusheng/imock