天天看點

JVM-SANDBOX子產品編寫EXAMPLEJVM-SANDBOX子產品編寫EXAMPLE

利用JVM-SANDBOX窺探JVM應用的SOCKET資料

JVM-SANDBOX是一個強大的AOP架構,既然是強大的,那我們就得用來做一些有别于其他架構的事情。究竟寫一個什麼作為入門例子比較好呢?既然要特别,那我們就來寫一個觀察JVM應用的SOCKET通訊的例子吧!

我們以JDK8(Hotsport)的Socket類為例,所有的SOCKET位元組流必定流經過<code>java.net.SocketInputStream</code>和<code>java.net.SocketOutputStream</code>,是以我們可以在這兩個類上做文章,以此來達到目的。

從<code>1.0.14</code>開始,JVM-SANDBOX釋出了<code>sandbox-module-starter</code>子產品,友善大家更快速的開發子產品。

首先我們建立一個maven工程:<code>sandbox-module-example</code>,這裡裝的是本次我們的例子

修改pom.xml

至此,一個JVM-SANDBOX子產品的工程架構就搭建起來了。

JVM-SANDBOX的子產品要求符合SPI規範,即

必須實作<code>com.alibaba.jvm.sandbox.api.Module</code>接口

必須擁有無參構造函數

必須配置<code>META-INFO/services/</code>

實戰中,大家都被Spring折騰習慣了,是以很多人其實并不熟悉JSR的SPI規範。導緻很多卡在入門的第一步。 從<code>1.0.14</code>版本開始,你将不需要配置<code>META-INFO/services/</code>了,隻需要給你的module加上注解即可

我們開始編寫一個子產品類,既然是一個統計SOCKET流量的例子,那我們就起一個風騷的名字,叫:"SOCKET守望者",SocketWatchman,簡稱:ExpSocketWm

一個MODULE要發揮作用,必定需要經過三個步驟:

子產品JAR包加載到容器,并完成類初始化和依賴注入工作

對觀察類進行插樁操作(如有)

激活子產品,使得子產品能接受Event(如有)

同樣的,一個MODULE需要被解除安裝時,反向執行對應的步驟

當機子產品,子產品将無法接收到觀察類的Event

對觀察類進行反向插樁,消除本子產品對類的影響

子產品類從容器中移除,如若目前子產品是JAR檔案的最後一個子產品,則将整個ModuleClassLoader進行銷毀

加載和解除安裝的生命周期被定義在<code>com.alibaba.jvm.sandbox.api.ModuleLifecycle</code>中

我們的SOCKET守望者因為觀察的是底層SOCKET流量,是以性能不得不考慮進來。理論上性能最好的是直接使用EventListener,但這裡隻是一個示範,重點是功能的表達,是以采用了AdviceListener性能稍差的監聽器。

同時,流量觀察隻有在有需要的時候才會觸發,是以做了延時激活功能。這樣在不觀察SOCKET流量時将不會有Advice産生,進一步降低性能開銷的影響。

例子中采用<code>EventWatchBuilder</code>來完成本次監聽SOCKET的示範,根據上邊的代碼分析,我們隻需要監聽

java.net.SocketInputStream的int:read(byte[],int,int,int);

java.net.SocketOutputStream的void:socketWrite(byte[],int,int);

這兩個方法即可

<code>new EventWatchBuilder(moduleEventWatcher)</code>構造一個事件觀察者的構造器,通過Builder我們可以友善的構造出我們的觀察者。

EventWatchBuilder類有2類核心的方法

onXXX

on開頭的方法表示構造進入一個新的内容,比如

onClass():

表示接下來需要對class進行篩選,在SocketWm例子中我們指定了類名作為篩選條件。

因為<code>java.net.SocketInputStream</code>在JDK中,由BootstrapClassLoader負責加載,預設情況下是不會檢索這個ClassLoader所加載的類。是以必須帶上<code>includeBootstrap()</code>明确下類的檢索範圍。

onBehavior():

表示需要對上一環節onClass()所比對的類進行方法級的篩選。在JDK中,是嚴格區分構造方法和普通方法的,但實際使用上,我們可以把他們兩個都抽象為類的行為(Behavior)。

其中構造方法的方法名為<code>&lt;init&gt;</code>,普通方法的方法名保持不變。

withXXX

with開頭的表示對目前on所比對的内容進行篩選,在SocketWm例子中,我們用<code>withParameterTypes(...)</code>對比對的行為做進一步篩選

IoPipe是我們定義出來感覺資料流的一個接口,通過這個接口,你除了能感覺流量吞吐之外,還能有機會窺探到SOCKET流經的資料

SocketWatcher是對不同廠商、不同版本JDK實作隔離的抽象接口。因為SocketInputStream和SocketOutputStream都不是public的類,說不準哪天JDK代碼重構中就有可能幹掉,是以我們必須要考慮到相容不同JDK版本的需要。

因為這個例子隻是一個示範,是以我選擇了JDK8來進行對标。這兩個類從JDK7開始就沒有什麼變化,是以雖然用的JDK8對标,但可以适用在JDK7+~JDK8的版本。JDK6是肯定不行,有興趣的可以自己去實作。

這個例子中我們是用MAVEN來組織我們的工程的,是以打包環節我們也繼續使用maven相關指令。

由于pom繼承了<code>sandbox-module-starter</code>,子產品的很多插件要求都在這個pom中幫你打點好了。你隻需要執行:

接下來就是收獲的季節

<code>sandbox-module-example-1.0.0-SNAPSHOT-jar-with-dependencies.jar</code>就是我們最終輸出的子產品JAR包。

把<code>sandbox-module-example-1.0.0-SNAPSHOT-jar-with-dependencies.jar</code>檔案放在<code>${HOME}/.sandbox-module/</code>目錄下

這樣,sandbox就會安裝到 <code>${HOME}/opt/sandbox</code>檔案夾中

找一個有流量的JVM應用,我在本地啟了一個WEB應用,把JVM-SANDBOX挂上。

可以看到ExpSocketWm已經加載進來,但是<code>FROZEN</code>狀态,增強了2個類2個方法,嗯,符合我們的預期。

因為ExpSocketWm是觀察時才激活,是以這裡我們需要開始觀察流量,類的狀态才會變更為<code>ACTIVE</code>

NICE,正常工作。

ExpSocketWm是我自己學習JVM-SANDBOX所聯系的一個例子,其實分發包中自帶了不少的Example,也都是學習的好資料

例子

例子說明

<a href="https://github.com/alibaba/jvm-sandbox/blob/master/sandbox-debug-module/src/main/java/com/alibaba/jvm/sandbox/module/debug/DebugWatchModule.java">DebugWatchModule.java</a>

模仿GREYS的watch指令

<a href="https://github.com/alibaba/jvm-sandbox/blob/master/sandbox-debug-module/src/main/java/com/alibaba/jvm/sandbox/module/debug/DebugTraceModule.java">DebugTraceModule.java</a>

模仿GREYES的trace指令

<a href="https://github.com/alibaba/jvm-sandbox/blob/master/sandbox-debug-module/src/main/java/com/alibaba/jvm/sandbox/module/debug/DebugRalphModule.java">DebugRalphModule.java</a>

無敵破壞王,故障注入(延時、熔斷、并發限流、TPS限流)

<a href="https://github.com/alibaba/jvm-sandbox/blob/master/sandbox-debug-module/src/main/java/com/alibaba/jvm/sandbox/module/debug/ExceptionLoggerModule.java">ExceptionLoggerModule.java</a>

記錄下你的應用都發生了哪些異常

$HOME/logs/sandbox/debug/exception-monitor.log

<a href="https://github.com/alibaba/jvm-sandbox/blob/master/sandbox-debug-module/src/main/java/com/alibaba/jvm/sandbox/module/debug/HttpHttpAccessLoggerModule.java">HttpHttpAccessLoggerModule.java</a>

記錄下你的應用的HTTP服務請求

$HOME/logs/sandbox/debug/servlet-monitor.log

<a href="https://github.com/alibaba/jvm-sandbox/blob/master/sandbox-debug-module/src/main/java/com/alibaba/jvm/sandbox/module/debug/JdbcLoggerModule.java">JdbcLoggerModule.java</a>

記錄下你的應用資料庫通路請求

$HOME/logs/sandbox/debug/jdbc-monitor.log

<a href="https://github.com/alibaba/jvm-sandbox/blob/master/sandbox-debug-module/src/main/java/com/alibaba/jvm/sandbox/module/debug/SpringLoggerModule.java">SpringLoggerModule.java</a>

記錄下Spring相關請求

$HOME/logs/sandbox/debug/spring-monitor.log