-
Notifications
You must be signed in to change notification settings - Fork 1.6k
FIRST MODULE
让我们来快速开始写一个入门模块
我们定义了一个钟,期望可以实现每隔一定的时间进行报时。
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();
}
}
很明显,这个钟的实现有问题,运行起来没有正确的报时,但却一直在报异常!
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)
问题出在了checkState()方法上,这个方法中抛出了异常。接下来我们通过构建一个沙箱模块作为例子,修复这个损坏的钟!
如果能直接修改checkState()方法的执行逻辑,让其不再抛异常,而是直接返回,那么一切就迎刃而解。但如何在不修改目标代码、不重启目标应用的情况下实现这个功能呢?
创建一个Java工程clock-tinker
假设用的是MAVEN,这里通过将parent指向sandbox-module-starter来简化我们的配置工作
<parent>
<groupId>com.alibaba.jvm.sandbox</groupId>
<artifactId>sandbox-module-starter</artifactId>
<version>1.2.0</version>
</parent>
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;
@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);
}
});
}
}
-
运行命令完成打包
mvn clean package
-
将打好的包复制到用户模块目录下
cp target/clock-tinker-1.0-SNAPSHOT-jar-with-dependencies.jar ~/.sandbox-module/
-
下载并安装最新版本沙箱:
-
下载地址:https://ompc.oss.aliyuncs.com/jvm-sandbox/release/sandbox-stable-bin.zip
-
执行安装
unzip sandbox-stable-bin.zip cd sandbox
-
-
启动沙箱
假设目标进程号:64229
-
启动沙箱
./sandbox.sh -p 64229 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 [email protected] sandbox-module-mgr ACTIVE LOADED 0 0 0.0.2 [email protected] sandbox-control ACTIVE LOADED 0 0 0.0.3 [email protected] total=4
可以看到broken-clock-tinker模块已经正确被沙箱所加载
-
接下来就是重头戏,如何在不影响目标应用的情况下,无声无息的修复这个故障!
触发broken-clock-tinker模块的repairCheckState(),让修复逻辑生效!
./sandbox.sh -p 64229 -d 'broken-clock-tinker/repairCheckState'
过一会,模块生效完成,你就会发现原本一直抛异常的钟已经开始在刷新时间了,
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
当你卸载掉JVM-SANDBOX时候,你就会发现原本已经被修复好的钟,又开始继续报错了。原因是原来通过clock-tinker模块修复的checkState()方法随着沙箱的卸载又恢复成原来错误的代码流程。
-
卸载沙箱
./sandbox.sh -p 64229 -S jvm-sandbox[default] shutdown finished.
-
故障继续
2018-10-23 23:44:10 2018-10-23 23:44:11 2018-10-23 23:44:12 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(Unknown Source) at com.taobao.demo.Clock.main(Unknown Source) 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(Unknown Source) at com.taobao.demo.Clock.main(Unknown Source) 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(Unknown Source) at com.taobao.demo.Clock.main(Unknown Source)
如何调试这一类的程序代码,可以参考GREYS项目中的一个ISSUES:怎么调试啊?
在这个教程中给大家演示了如何利用沙箱的模块改变了原有方法的执行流程,这里涉及到了沙箱最核心的类ModuleEventWatcher,这个类的实现可以通过@Resource
注释注入进来。
通过在THROWS
事件环节的改变流程,可以让原本应该抛出异常的checkState()
方法转变为正常返回值。你甚至可以窥探、篡改入参、返回值、抛出的异常等等,这些都将可以通过沙箱模块来实现。沙箱模块还能帮你实现很多有意思的功能,期待你的想象。
沙箱分发包中自带了实用工具的例子./example/sandbox-debug-module.jar
,代码在沙箱的sandbox-debug-module
模块,也是非常不错的实用工具和学习资料。
例子 | 例子说明 |
---|---|
DebugWatchModule.java | 模仿GREYS的watch 命令 |
DebugTraceModule.java | 模仿GREYES的trace 命令 |
DebugRalphModule.java | 无敌破坏王,故障注入(延时、熔断、并发限流、TPS限流) |
LogExceptionModule.java | 记录下你的应用都发生了哪些异常 $HOME/logs/sandbox/debug/exception-monitor.log |
LogServletAccessModule.java | 记录下你的应用的HTTP服务请求 $HOME/logs/sandbox/debug/servlet-access.log |
JVM沙箱偏向于底层产品,受众面比较窄,问题反馈沟通很可能会因为各种原因造成不及时。所以我们建立了一个钉钉小群,方便大家在这里进行沟通。