利用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><init></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