├── .gitignore ├── src └── main │ └── java │ └── com │ └── alibaba │ └── jvm │ └── sandbox │ └── module │ └── exampe │ ├── package-info.java │ └── tcpwm │ ├── IoPipe.java │ ├── SocketWatcher.java │ ├── impl │ └── SocketWatcherImplHotspotJDK8.java │ └── SocketWatchmanModule.java ├── pom.xml └── README.md /.gitignore: -------------------------------------------------------------------------------- 1 | ## for IDEA 2 | .idea 3 | *.iml 4 | 5 | ## for maven 6 | target 7 | pom.xml.versionsBackup 8 | -------------------------------------------------------------------------------- /src/main/java/com/alibaba/jvm/sandbox/module/exampe/package-info.java: -------------------------------------------------------------------------------- 1 | /** 2 | * 这个包中是我学习JVM-SANDBOX的各种例子 3 | *

4 | * jvm-sandbox是一个非常优秀的AOP框架,值得我一看 5 | */ 6 | package com.alibaba.jvm.sandbox.module.exampe; -------------------------------------------------------------------------------- /src/main/java/com/alibaba/jvm/sandbox/module/exampe/tcpwm/IoPipe.java: -------------------------------------------------------------------------------- 1 | package com.alibaba.jvm.sandbox.module.exampe.tcpwm; 2 | 3 | /** 4 | * IO管道 5 | */ 6 | public interface IoPipe { 7 | 8 | /** 9 | * 读 10 | * 11 | * @param buf 数据缓冲 12 | * @param off 偏移量 13 | * @param len 读出长度 14 | */ 15 | void read(byte buf[], int off, int len); 16 | 17 | /** 18 | * 写 19 | * 20 | * @param buf 数据缓冲 21 | * @param off 偏移量 22 | * @param len 写入长度 23 | */ 24 | void write(byte buf[], int off, int len); 25 | 26 | } 27 | -------------------------------------------------------------------------------- /src/main/java/com/alibaba/jvm/sandbox/module/exampe/tcpwm/SocketWatcher.java: -------------------------------------------------------------------------------- 1 | package com.alibaba.jvm.sandbox.module.exampe.tcpwm; 2 | 3 | import com.alibaba.jvm.sandbox.api.resource.ModuleEventWatcher; 4 | import com.alibaba.jvm.sandbox.module.exampe.tcpwm.impl.SocketWatcherImplHotspotJDK8; 5 | 6 | /** 7 | * TCP观察者 8 | */ 9 | public interface SocketWatcher { 10 | 11 | /** 12 | * 观察IO 13 | * 14 | * @param ioPipe IO管道 15 | */ 16 | void watching(IoPipe ioPipe); 17 | 18 | class Factory { 19 | 20 | static SocketWatcher make(final ModuleEventWatcher moduleEventWatcher) { 21 | // 各种平台适配... 22 | return new SocketWatcherImplHotspotJDK8(moduleEventWatcher); 23 | } 24 | 25 | } 26 | 27 | } 28 | -------------------------------------------------------------------------------- /pom.xml: -------------------------------------------------------------------------------- 1 | 3 | 4 | 4.0.0 5 | 6 | 7 | com.alibaba.jvm.sandbox 8 | sandbox-module-starter 9 | 1.0.16 10 | 11 | 12 | com.alibaba.jvm.sandbox.module.exampe 13 | sandbox-module-example 14 | 1.0.0-SNAPSHOT 15 | 16 | jar 17 | sandbox-module-example 18 | 19 | 20 | 21 | org.apache.commons 22 | commons-lang3 23 | 3.4 24 | 25 | 26 | 27 | 28 | -------------------------------------------------------------------------------- /src/main/java/com/alibaba/jvm/sandbox/module/exampe/tcpwm/impl/SocketWatcherImplHotspotJDK8.java: -------------------------------------------------------------------------------- 1 | package com.alibaba.jvm.sandbox.module.exampe.tcpwm.impl; 2 | 3 | import com.alibaba.jvm.sandbox.api.listener.ext.Advice; 4 | import com.alibaba.jvm.sandbox.api.listener.ext.AdviceListener; 5 | import com.alibaba.jvm.sandbox.api.listener.ext.EventWatchBuilder; 6 | import com.alibaba.jvm.sandbox.api.resource.ModuleEventWatcher; 7 | import com.alibaba.jvm.sandbox.module.exampe.tcpwm.IoPipe; 8 | import com.alibaba.jvm.sandbox.module.exampe.tcpwm.SocketWatcher; 9 | 10 | /** 11 | * Hotspot.JDK8版本下的Socket守望者实现 12 | */ 13 | public class SocketWatcherImplHotspotJDK8 implements SocketWatcher { 14 | 15 | private final ModuleEventWatcher moduleEventWatcher; 16 | 17 | public SocketWatcherImplHotspotJDK8(final ModuleEventWatcher moduleEventWatcher) { 18 | this.moduleEventWatcher = moduleEventWatcher; 19 | } 20 | 21 | @Override 22 | public void watching(final IoPipe ioPipe) { 23 | 24 | new EventWatchBuilder(moduleEventWatcher) 25 | .onClass("java.net.SocketInputStream").includeBootstrap() 26 | /**/.onBehavior("read").withParameterTypes(byte[].class, int.class, int.class, int.class) 27 | .onClass("java.net.SocketOutputStream").includeBootstrap() 28 | /**/.onBehavior("socketWrite").withParameterTypes(byte[].class, int.class, int.class) 29 | .onWatch(new AdviceListener() { 30 | @Override 31 | protected void afterReturning(Advice advice) { 32 | final String behaviorName = advice.getBehavior().getName(); 33 | if ("read".equals(behaviorName)) { 34 | ioPipe.read( 35 | (byte[]) advice.getParameterArray()[0], 36 | (Integer) advice.getParameterArray()[1], 37 | (Integer) advice.getReturnObj() 38 | ); 39 | } else if ("socketWrite".equals(behaviorName)) { 40 | ioPipe.write( 41 | (byte[]) advice.getParameterArray()[0], 42 | (Integer) advice.getParameterArray()[1], 43 | (Integer) advice.getParameterArray()[2] 44 | ); 45 | } 46 | } 47 | 48 | }); 49 | 50 | 51 | } 52 | 53 | } 54 | -------------------------------------------------------------------------------- /src/main/java/com/alibaba/jvm/sandbox/module/exampe/tcpwm/SocketWatchmanModule.java: -------------------------------------------------------------------------------- 1 | package com.alibaba.jvm.sandbox.module.exampe.tcpwm; 2 | 3 | import com.alibaba.jvm.sandbox.api.Information; 4 | import com.alibaba.jvm.sandbox.api.Module; 5 | import com.alibaba.jvm.sandbox.api.ModuleException; 6 | import com.alibaba.jvm.sandbox.api.ModuleLifecycle; 7 | import com.alibaba.jvm.sandbox.api.http.Http; 8 | import com.alibaba.jvm.sandbox.api.http.printer.ConcurrentLinkedQueuePrinter; 9 | import com.alibaba.jvm.sandbox.api.http.printer.Printer; 10 | import com.alibaba.jvm.sandbox.api.resource.ModuleController; 11 | import com.alibaba.jvm.sandbox.api.resource.ModuleEventWatcher; 12 | import org.kohsuke.MetaInfServices; 13 | 14 | import javax.annotation.Resource; 15 | import javax.servlet.http.HttpServletResponse; 16 | import java.io.IOException; 17 | import java.util.ArrayList; 18 | import java.util.List; 19 | import java.util.Vector; 20 | import java.util.concurrent.atomic.AtomicLong; 21 | import java.util.concurrent.locks.Condition; 22 | import java.util.concurrent.locks.ReadWriteLock; 23 | import java.util.concurrent.locks.ReentrantReadWriteLock; 24 | 25 | import static java.lang.Thread.currentThread; 26 | import static java.util.concurrent.TimeUnit.MILLISECONDS; 27 | 28 | /** 29 | * JVM-SANDBOX练手模块: 30 | * Socket守望者 31 | */ 32 | @MetaInfServices(Module.class) 33 | @Information(id = "ExpSocketWm", isActiveOnLoad = false, author = "oldmanpushcart@gmail.com", version = "0.0.6") 34 | public class SocketWatchmanModule implements Module, ModuleLifecycle, IoPipe, Runnable { 35 | 36 | // 注入模块控制器(控制当前模块状态) 37 | @Resource 38 | private ModuleController moduleController; 39 | 40 | // 注入模块观察者 41 | @Resource 42 | private ModuleEventWatcher moduleEventWatcher; 43 | 44 | private final List printers = new ArrayList(); 45 | 46 | @Override 47 | public void loadCompleted() { 48 | 49 | // 启动输出线程 50 | final Thread thread = new Thread(this,"socket-watchman-reporter"); 51 | thread.setDaemon(true); 52 | thread.start(); 53 | 54 | 55 | // 代码插桩 56 | SocketWatcher.Factory 57 | .make(moduleEventWatcher) 58 | .watching(this); 59 | } 60 | 61 | @Http("/show") 62 | public void show(final HttpServletResponse resp) throws ModuleException, IOException { 63 | 64 | // 注册Printer 65 | final Printer printer; 66 | synchronized (printers) { 67 | printers.add(printer = new ConcurrentLinkedQueuePrinter(resp.getWriter())); 68 | } 69 | 70 | moduleController.active(); 71 | printer.println("SocketWatchman is working.\nPress CTRL_C abort it!"); 72 | try { 73 | printer.waitingForBroken(); 74 | } finally { 75 | moduleController.frozen(); 76 | synchronized(printers) { 77 | printers.remove(printer); 78 | } 79 | } 80 | } 81 | 82 | /* 83 | * 5秒刷新一次 84 | */ 85 | private static final long intervalMs = 1000 * 5; 86 | private final ReadWriteLock rwLock = new ReentrantReadWriteLock(); 87 | private final Condition intervalCondition = rwLock.writeLock().newCondition(); 88 | private final AtomicLong rByteCounter = new AtomicLong(); 89 | private final AtomicLong wByteCounter = new AtomicLong(); 90 | 91 | @Override 92 | public void read(byte[] buf, int off, int len) { 93 | rwLock.readLock().lock(); 94 | try { 95 | rByteCounter.addAndGet(len); 96 | } finally { 97 | rwLock.readLock().unlock(); 98 | } 99 | } 100 | 101 | @Override 102 | public void write(byte[] buf, int off, int len) { 103 | rwLock.readLock().lock(); 104 | try { 105 | wByteCounter.addAndGet(len); 106 | } finally { 107 | rwLock.readLock().unlock(); 108 | } 109 | } 110 | 111 | 112 | private volatile boolean isRunning = true; 113 | 114 | @Override 115 | public void run() { 116 | while (isRunning 117 | && !currentThread().isInterrupted()) { 118 | try { 119 | final long rByteCount; 120 | final long wByteCount; 121 | rwLock.writeLock().lock(); 122 | try { 123 | intervalCondition.await(intervalMs, MILLISECONDS); 124 | rByteCount = rByteCounter.getAndSet(0); 125 | wByteCount = wByteCounter.getAndSet(0); 126 | } finally { 127 | rwLock.writeLock().unlock(); 128 | } 129 | 130 | final String msg = String.format("" 131 | + " READ : RATE=%.2f(kb)/sec ; TOTAL=%.2f(kb)\n" 132 | + "WRITE : RATE=%.2f(kb)/sec ; TOTAL=%.2f(kb)\n" 133 | + "statistics in %s(sec).\n", 134 | 1f * rByteCount / intervalMs, 1f * rByteCount / 1024, 135 | 1f * wByteCount / intervalMs, 1f * wByteCount / 1024, 136 | intervalMs / 1000 137 | ); 138 | 139 | synchronized(printers) { 140 | for (final Printer printer : printers) { 141 | try { 142 | printer.println(msg); 143 | } catch (Throwable cause) { 144 | // ignore... 145 | } 146 | } 147 | } 148 | 149 | } catch (InterruptedException e) { 150 | Thread.currentThread().interrupt(); 151 | } 152 | } 153 | } 154 | 155 | @Override 156 | public void onLoad() throws Throwable { 157 | isRunning = true; 158 | } 159 | 160 | @Override 161 | public void onUnload() throws Throwable { 162 | isRunning = false; 163 | } 164 | 165 | 166 | // -- 暂时不用 167 | 168 | @Override 169 | public void onActive() throws Throwable { 170 | 171 | } 172 | 173 | @Override 174 | public void onFrozen() throws Throwable { 175 | 176 | } 177 | } 178 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # JVM-SANDBOX模块编写EXAMPLE 2 | 3 | > 利用JVM-SANDBOX窥探JVM应用的SOCKET数据 4 | 5 | ## 开始编写一个MODULE 6 | 7 | JVM-SANDBOX是一个强大的AOP框架,既然是强大的,那我们就得用来做一些有别于其他框架的事情。究竟写一个什么作为入门例子比较好呢?既然要特别,那我们就来写一个观察JVM应用的SOCKET通讯的例子吧! 8 | 9 | ## JDK8(Hotspot)的SOCKET类分析 10 | 11 | 我们以JDK8(Hotsport)的Socket类为例,所有的SOCKET字节流必定流经过`java.net.SocketInputStream`和`java.net.SocketOutputStream`,所以我们可以在这两个类上做文章,以此来达到目的。 12 | 13 | ![](https://github.com/alibaba/jvm-sandbox/wiki/img/FOR-EXAMPLE-001-SOCKET.png) 14 | 15 | ## 开始搭建工程 16 | 17 | 从`1.0.14`开始,JVM-SANDBOX释出了`sandbox-module-starter`模块,方便大家更快速的开发模块。 18 | 19 | ```xml 20 | 21 | com.alibaba.jvm.sandbox 22 | sandbox-module-starter 23 | 1.0.14 24 | 25 | ``` 26 | 27 | ### 构建POM 28 | 29 | - 首先我们创建一个maven工程:`sandbox-module-example`,这里装的是本次我们的例子 30 | 31 | ```shell 32 | mvn archetype:generate -DgroupId=com.alibaba.jvm.sandbox.module.exampe -DartifactId=sandbox-module-example -DarchetypeArtifactId=maven-archetype-quickstart -DinteractiveMode=false 33 | ``` 34 | 35 | - 修改pom.xml 36 | 37 | ```xml 38 | 40 | 41 | 4.0.0 42 | 43 | 44 | com.alibaba.jvm.sandbox 45 | sandbox-module-starter 46 | 1.0.14 47 | 48 | 49 | com.alibaba.jvm.sandbox.module.exampe 50 | sandbox-module-example 51 | 1.0.0-SNAPSHOT 52 | 53 | jar 54 | sandbox-module-example 55 | 56 | 57 | 58 | org.apache.commons 59 | commons-lang3 60 | 3.4 61 | 62 | 63 | 64 | 65 | ``` 66 | 67 | 至此,一个JVM-SANDBOX模块的工程框架就搭建起来了。 68 | 69 | ### 第一个MODULE 70 | 71 | JVM-SANDBOX的模块要求符合SPI规范,即 72 | 73 | 1. **必须实现`com.alibaba.jvm.sandbox.api.Module`接口** 74 | 1. **必须拥有无参构造函数** 75 | 1. **必须配置`META-INF/services/`** 76 | > 实战中,大家都被Spring折腾习惯了,所以很多人其实并不熟悉JSR的SPI规范。导致很多卡在入门的第一步。 77 | 78 | > 从`1.0.14`版本开始,你将不需要配置`META-INF/services/`了,只需要给你的module加上注解即可 79 | 80 | > ```java 81 | > @MetaInfServices(Module.class) 82 | > ``` 83 | 84 | 我们开始编写一个模块类,既然是一个统计SOCKET流量的例子,那我们就起一个风骚的名字,叫:"SOCKET守望者",SocketWatchman,简称:ExpSocketWm 85 | 86 | ```java 87 | 88 | package com.alibaba.jvm.sandbox.module.exampe.tcpwm; 89 | 90 | /** 91 | * JVM-SANDBOX练手模块: 92 | * Socket守望者 93 | */ 94 | @MetaInfServices(Module.class) 95 | @Information(id = "ExpSocketWm", isActiveOnLoad = false, author = "oldmanpushcart@gmail.com", version = "0.0.5") 96 | public class SocketWatchmanModule implements Module, ModuleLifecycle, IoPipe, Runnable { 97 | 98 | @Override 99 | public void loadCompleted() { 100 | 101 | } 102 | 103 | } 104 | ``` 105 | 106 | ### MODULE的生命周期 107 | 108 | - 一个MODULE要发挥作用,必定需要经过三个步骤: 109 | 110 | 1. 模块JAR包加载到容器,并完成类初始化和依赖注入工作 111 | 1. 对观察类进行插桩操作(如有) 112 | 1. 激活模块,使得模块能接受Event(如有) 113 | 114 | - 同样的,一个MODULE需要被卸载时,反向执行对应的步骤 115 | 116 | 1. 冻结模块,模块将无法接收到观察类的Event 117 | 1. 对观察类进行反向插桩,消除本模块对类的影响 118 | 1. 模块类从容器中移除,如若当前模块是JAR文件的最后一个模块,则将整个ModuleClassLoader进行销毁 119 | 120 | 加载和卸载的生命周期被定义在`com.alibaba.jvm.sandbox.api.ModuleLifecycle`中 121 | 122 | #### 模块生命周期 123 | 124 | ![](https://github.com/alibaba/jvm-sandbox/wiki/img/MODULE_LIFECYCLE-001.png) 125 | 126 | #### 模块加载核心流程 127 | 128 | ![](https://github.com/alibaba/jvm-sandbox/wiki/img/MODULE_LIFECYCLE-002.png) 129 | 130 | 我们的SOCKET守望者因为观察的是底层SOCKET流量,所以性能不得不考虑进来。理论上性能最好的是直接使用EventListener,但这里只是一个演示,重点是功能的表达,所以采用了AdviceListener性能稍差的监听器。 131 | 132 | 同时,流量观察只有在有需要的时候才会触发,所以做了延时激活功能。这样在不观察SOCKET流量时将不会有Advice产生,进一步降低性能开销的影响。 133 | 134 | ### 代码织入 135 | 136 | 例子中采用`EventWatchBuilder`来完成本次监听SOCKET的演示,根据上边的代码分析,我们只需要监听 137 | 138 | - java.net.SocketInputStream的int:read(byte[],int,int,int); 139 | - java.net.SocketOutputStream的void:socketWrite(byte[],int,int); 140 | 141 | 这两个方法即可 142 | 143 | ```java 144 | @Override 145 | public void watching(final IoPipe ioPipe) { 146 | 147 | new EventWatchBuilder(moduleEventWatcher) 148 | .onClass("java.net.SocketInputStream").includeBootstrap() 149 | /**/.onBehavior("read").withParameterTypes(byte[].class, int.class, int.class, int.class) 150 | .onClass("java.net.SocketOutputStream").includeBootstrap() 151 | /**/.onBehavior("socketWrite").withParameterTypes(byte[].class, int.class, int.class) 152 | .onWatch(new AdviceListener() { 153 | 154 | final String MARK_R = "MARK_R"; 155 | final String MARK_W = "MARK_W"; 156 | 157 | @Override 158 | protected void before(Advice advice) { 159 | final String behaviorName = advice.getBehavior().getName(); 160 | if ("read".equals(behaviorName)) { 161 | advice.mark(MARK_R); 162 | } else if ("socketWrite".equals(behaviorName)) { 163 | advice.mark(MARK_W); 164 | } 165 | } 166 | 167 | @Override 168 | protected void afterReturning(Advice advice) { 169 | if (advice.hasMark(MARK_R)) { 170 | ioPipe.read( 171 | (byte[]) advice.getParameterArray()[0], 172 | (Integer) advice.getParameterArray()[1], 173 | (Integer) advice.getReturnObj() 174 | ); 175 | } else if (advice.hasMark(MARK_W)) { 176 | ioPipe.write( 177 | (byte[]) advice.getParameterArray()[0], 178 | (Integer) advice.getParameterArray()[1], 179 | (Integer) advice.getParameterArray()[2] 180 | ); 181 | } 182 | } 183 | 184 | }); 185 | } 186 | ``` 187 | 188 | #### watching()方法代码解读 189 | 190 | `new EventWatchBuilder(moduleEventWatcher)`构造一个事件观察者的构造器,通过Builder我们可以方便的构造出我们的观察者。 191 | 192 | EventWatchBuilder类有2类核心的方法 193 | 194 | - **onXXX** 195 | 196 | on开头的方法表示构造进入一个新的内容,比如 197 | 198 | - **onClass():** 199 | 200 | 表示接下来需要对class进行筛选,在SocketWm例子中我们指定了类名作为筛选条件。 201 | 202 | 因为`java.net.SocketInputStream`在JDK中,由BootstrapClassLoader负责加载,默认情况下是不会检索这个ClassLoader所加载的类。所以必须带上`includeBootstrap()`明确下类的检索范围。 203 | 204 | - **onBehavior():** 205 | 206 | 表示需要对上一环节onClass()所匹配的类进行方法级的筛选。在JDK中,是严格区分构造方法和普通方法的,但实际使用上,我们可以把他们两个都抽象为类的行为(Behavior)。 207 | 208 | 其中构造方法的方法名为``,普通方法的方法名保持不变。 209 | 210 | - **withXXX** 211 | 212 | with开头的表示对当前on所匹配的内容进行筛选,在SocketWm例子中,我们用`withParameterTypes(...)`对匹配的行为做进一步筛选 213 | 214 | ```java 215 | .onBehavior("socketWrite") 216 | .withParameterTypes(byte[].class, int.class, int.class) 217 | ``` 218 | 219 | 220 | #### 其他辅助类解读 221 | 222 | - IoPipe是我们定义出来感知数据流的一个接口,通过这个接口,你除了能感知流量吞吐之外,还能有机会窥探到SOCKET流经的数据 223 | 224 | ```java 225 | /** 226 | * IO管道 227 | */ 228 | public interface IoPipe { 229 | 230 | /** 231 | * 读 232 | * 233 | * @param buf 数据缓冲 234 | * @param off 偏移量 235 | * @param len 读出长度 236 | */ 237 | void read(byte buf[], int off, int len); 238 | 239 | /** 240 | * 写 241 | * 242 | * @param buf 数据缓冲 243 | * @param off 偏移量 244 | * @param len 写入长度 245 | */ 246 | void write(byte buf[], int off, int len); 247 | 248 | } 249 | ``` 250 | 251 | - SocketWatcher是对不同厂商、不同版本JDK实现隔离的抽象接口。因为SocketInputStream和SocketOutputStream都不是public的类,说不准哪天JDK代码重构中就有可能干掉,所以我们必须要考虑到兼容不同JDK版本的需要。 252 | 253 | ```java 254 | /** 255 | * TCP观察者 256 | */ 257 | public interface SocketWatcher { 258 | 259 | /** 260 | * 观察IO吞吐量 261 | * 262 | * @param ioThroughput IO吞吐 263 | */ 264 | void watching(IoPipe ioThroughput); 265 | 266 | class Factory { 267 | 268 | static SocketWatcher make(final ModuleEventWatcher moduleEventWatcher) { 269 | // 各种平台适配... 270 | return new SocketWatcherImplHotspotJDK8(moduleEventWatcher); 271 | } 272 | 273 | } 274 | 275 | } 276 | ``` 277 | 278 | 因为这个例子只是一个演示,所以我选择了JDK8来进行对标。这两个类从JDK7开始就没有什么变化,所以虽然用的JDK8对标,但可以适用在JDK7+~JDK8的版本。JDK6是肯定不行,有兴趣的可以自己去实现。 279 | 280 | ## 部署运行 281 | 282 | ### 编译打包 283 | 284 | 这个例子中我们是用MAVEN来组织我们的工程的,所以打包环节我们也继续使用maven相关命令。 285 | 286 | 由于pom继承了`sandbox-module-starter`,模块的很多插件要求都在这个pom中帮你打点好了。你只需要执行: 287 | 288 | ```shell 289 | mvn clean package 290 | ``` 291 | 292 | 接下来就是收获的季节 293 | 294 | ```shell 295 | duxiaokundeMacBook-Pro:target vlinux$ ls -lrt 296 | total 1056 297 | drwxr-xr-x 4 vlinux staff 128 2 27 22:50 classes 298 | drwxr-xr-x 3 vlinux staff 96 2 27 22:50 maven-status 299 | drwxr-xr-x 3 vlinux staff 96 2 27 22:50 generated-sources 300 | drwxr-xr-x 3 vlinux staff 96 2 27 22:50 maven-archiver 301 | -rw-r--r-- 1 vlinux staff 11868 2 27 22:50 sandbox-module-example-1.0.0-SNAPSHOT.jar 302 | drwxr-xr-x 4 vlinux staff 128 2 27 22:50 javadoc-bundle-options 303 | drwxr-xr-x 16 vlinux staff 512 2 27 22:50 apidocs 304 | -rw-r--r-- 1 vlinux staff 68238 2 27 22:50 sandbox-module-example-1.0.0-SNAPSHOT-javadoc.jar 305 | -rw-r--r-- 1 vlinux staff 7970 2 27 22:50 sandbox-module-example-1.0.0-SNAPSHOT-sources.jar 306 | drwxr-xr-x 2 vlinux staff 64 2 27 22:50 archive-tmp 307 | -rw-r--r-- 1 vlinux staff 447428 2 27 22:50 sandbox-module-example-1.0.0-SNAPSHOT-jar-with-dependencies.jar 308 | ``` 309 | 310 | `sandbox-module-example-1.0.0-SNAPSHOT-jar-with-dependencies.jar`就是我们最终输出的模块JAR包。 311 | 312 | ### 运行调试 313 | 314 | #### 部署模块包 315 | 316 | 把`sandbox-module-example-1.0.0-SNAPSHOT-jar-with-dependencies.jar`文件放在`${HOME}/.sandbox-module/`目录下 317 | 318 | #### 下载并安装JVM-SANDBOX 319 | 320 | 下载当前[稳定的JVM-SANDBOX容器版本](http://ompc.oss-cn-hangzhou.aliyuncs.com/jvm-sandbox/release/sandbox-stable-bin.zip),解压并安装 321 | 322 | ```shell 323 | unzip sandbox-stable-bin.zip 324 | cd sandbox 325 | ./install-local.sh -p ~/opt 326 | ``` 327 | 328 | 这样,sandbox就会安装到 `${HOME}/opt/sandbox`文件夹中 329 | 330 | #### 试运行 331 | 332 | 找一个有流量的JVM应用,我在本地启了一个WEB应用,把JVM-SANDBOX挂上。 333 | 334 | ```shell 335 | duxiaokundeMacBook-Pro:bin vlinux$ ./sandbox.sh -p 10491 -l 336 | debug-ralph ACTIVE LOADED 0 0 0.0.1 luanjia@taobao.com 337 | debug-exception-logger ACTIVE LOADED 1 5 0.0.1 luanjia@taobao.com 338 | debug-http-logger ACTIVE LOADED 2 4 0.0.1 luanjia@taobao.com 339 | debug-trace ACTIVE LOADED 0 0 0.0.1 luanjia@taobao.com 340 | debug-jdbc-logger ACTIVE LOADED 6 59 0.0.1 luanjia@taobao.com 341 | module-mgr ACTIVE LOADED 0 0 0.0.1 luanjia@taobao.com 342 | debug-watch ACTIVE LOADED 0 0 0.0.1 luanjia@taobao.com 343 | debug-spring-logger ACTIVE LOADED 3 10 0.0.1 luanjia@taobao.com 344 | control ACTIVE LOADED 0 0 0.0.1 luanjia@taobao.com 345 | ExpSocketWm FROZEN LOADED 2 2 0.0.5 oldmanpushcart@gmail.com 346 | info ACTIVE LOADED 0 0 0.0.3 luanjia@taobao.com 347 | total=11 348 | ``` 349 | 350 | 可以看到ExpSocketWm已经加载进来,但是`FROZEN`状态,增强了2个类2个方法,嗯,符合我们的预期。 351 | 352 | 因为ExpSocketWm是观察时才激活,所以这里我们需要开始观察流量,类的状态才会变更为`ACTIVE` 353 | 354 | ```shell 355 | duxiaokundeMacBook-Pro:bin vlinux$ ./sandbox.sh -p 10491 -d 'ExpSocketWm/show' 356 | SocketWatchman is working. 357 | Press CTRL_C abort it! 358 | READ : RATE=0.00(kb)/sec ; TOTAL=0.00(kb) 359 | WRITE : RATE=0.00(kb)/sec ; TOTAL=0.00(kb) 360 | statistics in 5(sec). 361 | 362 | READ : RATE=38.53(kb)/sec ; TOTAL=188.14(kb) 363 | WRITE : RATE=1.49(kb)/sec ; TOTAL=7.26(kb) 364 | statistics in 5(sec). 365 | 366 | ``` 367 | 368 | NICE,正常工作。 369 | 370 | ## 其他自带例子 371 | 372 | ExpSocketWm是我自己学习JVM-SANDBOX所联系的一个例子,其实分发包中自带了不少的Example,也都是学习的好资料 373 | 374 | |例子|例子说明| 375 | |---|:---| 376 | |[DebugWatchModule.java](https://github.com/alibaba/jvm-sandbox/blob/master/sandbox-debug-module/src/main/java/com/alibaba/jvm/sandbox/module/debug/DebugWatchModule.java)|模仿GREYS的watch命令| 377 | |[DebugTraceModule.java](https://github.com/alibaba/jvm-sandbox/blob/master/sandbox-debug-module/src/main/java/com/alibaba/jvm/sandbox/module/debug/DebugTraceModule.java)|模仿GREYES的trace命令| 378 | |[DebugRalphModule.java](https://github.com/alibaba/jvm-sandbox/blob/master/sandbox-debug-module/src/main/java/com/alibaba/jvm/sandbox/module/debug/DebugRalphModule.java)|无敌破坏王,故障注入(延时、熔断、并发限流、TPS限流)| 379 | |[ExceptionLoggerModule.java](https://github.com/alibaba/jvm-sandbox/blob/master/sandbox-debug-module/src/main/java/com/alibaba/jvm/sandbox/module/debug/ExceptionLoggerModule.java)|记录下你的应用都发生了哪些异常
$HOME/logs/sandbox/debug/exception-monitor.log| 380 | |[HttpHttpAccessLoggerModule.java](https://github.com/alibaba/jvm-sandbox/blob/master/sandbox-debug-module/src/main/java/com/alibaba/jvm/sandbox/module/debug/HttpHttpAccessLoggerModule.java)|记录下你的应用的HTTP服务请求
$HOME/logs/sandbox/debug/servlet-monitor.log| 381 | |[JdbcLoggerModule.java](https://github.com/alibaba/jvm-sandbox/blob/master/sandbox-debug-module/src/main/java/com/alibaba/jvm/sandbox/module/debug/JdbcLoggerModule.java)|记录下你的应用数据库访问请求
$HOME/logs/sandbox/debug/jdbc-monitor.log| 382 | |[SpringLoggerModule.java](https://github.com/alibaba/jvm-sandbox/blob/master/sandbox-debug-module/src/main/java/com/alibaba/jvm/sandbox/module/debug/SpringLoggerModule.java)|记录下Spring相关请求
$HOME/logs/sandbox/debug/spring-monitor.log| 383 | 384 | ## 写在后边 385 | 386 | 例子已经上传到我的GITHUB:[oldmanpushcart/sandbox-module-example](https://github.com/oldmanpushcart/sandbox-module-example) 387 | --------------------------------------------------------------------------------