├── .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 | 
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 | 
125 |
126 | #### 模块加载核心流程
127 |
128 | 
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 |
--------------------------------------------------------------------------------