<output id="qn6qe"></output>

    1. <output id="qn6qe"><tt id="qn6qe"></tt></output>
    2. <strike id="qn6qe"></strike>

      亚洲 日本 欧洲 欧美 视频,日韩中文字幕有码av,一本一道av中文字幕无码,国产线播放免费人成视频播放,人妻少妇偷人无码视频,日夜啪啪一区二区三区,国产尤物精品自在拍视频首页,久热这里只有精品12

      JVM Sandbox入門教程與原理淺談

      本文為本人原創(chuàng)文章,首發(fā)收錄于《阿里開發(fā)者》公眾號。

      在日常業(yè)務(wù)代碼開發(fā)中,我們經(jīng)常接觸到AOP,比如熟知的Spring AOP。我們用它來做業(yè)務(wù)切面,比如登錄校驗,日志記錄,性能監(jiān)控,全局過濾器等。但Spring AOP有一個局限性,并不是所有的類都托管在 Spring 容器中,例如很多中間件代碼、三方包代碼,Java原生代碼,都不能被Spring AOP代理到。如此一來,一旦你想要做的切面邏輯并不屬于Spring的管轄范圍,或者你想實現(xiàn)脫離Spring限制的切面功能,就無法實現(xiàn)了。

      那對于Java后端應(yīng)用,有沒有一種更為通用的AOP方式呢?答案是有的,Java自身提供了JVM TI,Instrumentation等功能,允許使用者以通過一系列API完成對JVM的復(fù)雜控制。自此衍生出了很多著名的框架,比如Btrace,Arthas等等,幫助開發(fā)者們實現(xiàn)更多更復(fù)雜的Java功能。

      JVM Sandbox也是其中的一員。當(dāng)然,不同框架的設(shè)計目的和使命是不一樣的,JVM-Sandbox的設(shè)計目的是實現(xiàn)一種在不重啟、不侵入目標(biāo)JVM應(yīng)用情況下的AOP解決方案。

      是不是看到這里還是不清楚我在講什么?別急,我舉幾個典型的JVM-Sandbox應(yīng)用場景:

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

      可以看到,借助JVM-Sandbox,你可以實現(xiàn)很多之前在業(yè)務(wù)代碼中做不了的事,大大拓展了可操作的范圍。

      本文圍繞JVM SandBox展開,主要介紹如下內(nèi)容:

      • JVM SandBox誕生背景
      • JVM SandBox架構(gòu)設(shè)計
      • JVM SandBox代碼實戰(zhàn)
      • JVM SandBox底層技術(shù)
      • 總結(jié)與展望

      JVM Sandbox誕生背景

      JVM Sandbox誕生的技術(shù)背景在引言中已經(jīng)贅述完畢,下面是作者開發(fā)該框架的一些業(yè)務(wù)背景,以下描述引用自文章:

      JVM SandBox 是阿里開源的一款 JVM 平臺非侵入式運行期 AOP 解決方案,本質(zhì)上是一種 AOP 落地形式。那么可能有同學(xué)會問:已有成熟的 Spring AOP 解決方案,阿里巴巴為什么還要“重復(fù)造輪子”?這個問題要回到 JVM SandBox 誕生的背景中來回答。在 2016 年中,天貓雙十一催動了阿里巴巴內(nèi)部大量業(yè)務(wù)系統(tǒng)的改動,恰逢徐冬晨(阿里巴巴測試開發(fā)專家)所在的團隊調(diào)整,測試資源保障嚴(yán)重不足,迫使他們必須考慮更精準(zhǔn)、更便捷的老業(yè)務(wù)測試回歸驗證方案。開發(fā)團隊面臨的是新接手的老系統(tǒng),老的業(yè)務(wù)代碼架構(gòu)難以滿足可測性的要求,很多現(xiàn)有測試框架也無法應(yīng)用到老的業(yè)務(wù)系統(tǒng)架構(gòu)中,于是需要新的測試思路和測試框架。

      為什么不采用 Spring AOP 方案呢?Spring AOP 方案的痛點在于不是所有業(yè)務(wù)代碼都托管在 Spring 容器中,而且更底層的中間件代碼、三方包代碼無法納入到回歸測試范圍,更糟糕的是測試框架會引入自身所依賴的類庫,經(jīng)常與業(yè)務(wù)代碼的類庫產(chǎn)生沖突,因此,JVM SandBox 應(yīng)運而生。

      JVM Sandbox整體架構(gòu)

      本章節(jié)不詳細(xì)講述JVM SandBox的所有架構(gòu)設(shè)計,只講其中幾個最重要的特性。詳細(xì)的架構(gòu)設(shè)計可以看原框架代碼倉庫的Wiki。

      類隔離

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

      • 和目標(biāo)應(yīng)用的類隔離:不用擔(dān)心加載沙箱會引起原應(yīng)用的類污染、沖突。
      • 模塊之間類隔離:做到模塊與模塊之間、模塊和沙箱之間、模塊和應(yīng)用之間互不干擾。

      無侵入AOP與事件驅(qū)動

      JVM-SANDBOX屬于基于Instrumentation的動態(tài)編織類的AOP框架,通過精心構(gòu)造了字節(jié)碼增強邏輯,使得沙箱的模塊能在不違反JDK約束情況下實現(xiàn)對目標(biāo)應(yīng)用方法的無侵入運行時AOP攔截。

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

      // BEFORE
      try {
      
         /*
          * do something...
          */
      
          // RETURN
          return;
      
      } catch (Throwable cause) {
          // THROWS
      }
      

      在沙箱的世界觀中,任何一個Java方法的調(diào)用都可以分解為BEFORE、RETURNTHROWS三個環(huán)節(jié),由此在三個環(huán)節(jié)上引申出對應(yīng)環(huán)節(jié)的事件探測和流程控制機制。

      基于BEFORE、RETURNTHROWS三個環(huán)節(jié)事件分離,沙箱的模塊可以完成很多類AOP的操作。

      1. 可以感知和改變方法調(diào)用的入?yún)?/li>
      2. 可以感知和改變方法調(diào)用返回值和拋出的異常
      3. 可以改變方法執(zhí)行的流程
        • 在方法體執(zhí)行之前直接返回自定義結(jié)果對象,原有方法代碼將不會被執(zhí)行
        • 在方法體返回之前重新構(gòu)造新的結(jié)果對象,甚至可以改變?yōu)閽伋霎惓?/li>
        • 在方法體拋出異常之后重新拋出新的異常,甚至可以改變?yōu)檎7祷?/li>

      一切都是事件驅(qū)動的,這一點你可能很迷糊,但是在下文的實戰(zhàn)環(huán)節(jié)中,可以幫助你理解。

      JVM Sandbox代碼實戰(zhàn)

      我將實戰(zhàn)章節(jié)提前到這里,目的是方便大家快速了解使用JVM SandBox開發(fā)是一件多么舒服的事情(相比于自己使用字節(jié)碼替換等工具)。

      使用版本:JVM-Sandbox 1.2.0

      官方源碼:https://github.com/alibaba/jvm-sandbox

      我們來實現(xiàn)一個小工具,在日常工作中,我們總會遇到一些巨大的Spring工程,里面有茫茫多的Bean和業(yè)務(wù)代碼,啟動一個工程可能需要5分鐘甚至更久,嚴(yán)重拖累開發(fā)效率。

      我們嘗試使用JVM Sandbox來開發(fā)一個工具,對應(yīng)用的Spring Bean啟動耗時進行一次統(tǒng)計。這樣能一目了然的發(fā)現(xiàn)工程啟動慢的主要原因,避免去盲人摸象的優(yōu)化。

      最終效果如圖:

      圖中統(tǒng)計了一個應(yīng)用從啟動開始到所有SpringBean的啟動耗時,按照從高到低排序,我由于是demo應(yīng)用,Bean的耗時都偏低(也沒有太多業(yè)務(wù)Bean),但在實際應(yīng)用中會有非常多幾秒甚至十幾秒才完成初始化的Bean,可以進行針對性優(yōu)化。

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

      先貼上思路的整體流程:

      首先新建Maven工程,在Maven依賴中引用JVM SandBox,官方推薦獨立工程使用parent方式。

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

      新建一個類作為一個JVM SandBox模塊,如下圖:

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

      • Agent:隨著JVM啟動一起啟動
      • Attach:在已經(jīng)運行的JVM進程中,動態(tài)的插入

      我們由于是監(jiān)控JVM啟動數(shù)據(jù),所以需要AGENT模式。

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

      其中ModuleLifecycle包含了整個模塊的生命周期回調(diào)函數(shù)。

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

      最常用的是loadCompleted,所以我們重寫loadCompleted類,在里面開啟我們的監(jiān)控類SpringBeanStartMonitor線程。

      而SpringBeanStartMonitor的核心代碼如下圖:

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

      使用doMethodFilter過濾出要監(jiān)聽的方法,這里是initializeBean。

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

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

      將我們的springBeanInitListener監(jiān)聽器綁定到被觀測的方法上。這樣每次initializeBean被調(diào)用,都會走到我們的監(jiān)聽器邏輯。

      監(jiān)聽器的主要邏輯如下:

      代碼有點長,不必細(xì)看,主要就是在原方法的BeforeEvent(進入前)和ReturnEvent(執(zhí)行正常返回后)執(zhí)行上述的切面邏輯,我這里便是使用了一個MAP存儲每個Bean的初始化開始和結(jié)束時間,最終統(tǒng)計出初始化耗時。

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

      我們通過一個簡陋的辦法,檢查http://127.0.0.1:8080/是否會返回小于500的狀態(tài)碼,來判斷Spring容器是否已經(jīng)啟動。當(dāng)然如果你的Spring沒有使用Web框架,就不能用這個方法來判斷啟動完成,你也許可以通過Spring自己的生命周期鉤子函數(shù)來實現(xiàn),這里我是偷了個懶。

      整個SpringBean監(jiān)聽模塊的開發(fā)就完成了,你可以感受到,你的開發(fā)和日常業(yè)務(wù)開發(fā)幾乎沒有區(qū)別,這就是JVM Sandbox帶給你的最大好處。

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

      https://github.com/monitor4all/javaMonitor

      JVM Sandbox底層技術(shù)

      整個JVM Sandbox的入門使用基本上講完了,上文提到了一些JVM技術(shù)名詞,可能小伙伴們聽過但不是特別了解。這里簡單闡述幾個重要的概念,理清楚這幾個概念之間的關(guān)系,以便大家更好的理解JVM Sandbox底層的實現(xiàn)。

      JVMTI

      JVMTI(JVM Tool Interface)是 Java 虛擬機所提供的 native 編程接口,JVMTI可以用來開發(fā)并監(jiān)控虛擬機,可以查看JVM內(nèi)部的狀態(tài),并控制JVM應(yīng)用程序的執(zhí)行??蓪崿F(xiàn)的功能包括但不限于:調(diào)試、監(jiān)控、線程分析、覆蓋率分析工具等。

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

      JavaAgent和Instrumentation

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

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

      在上面-javaagent參數(shù)中提到了參閱java.lang.instrument,這是在rt.jar 中定義的一個包,該包提供了一些工具幫助開發(fā)人員在 Java 程序運行時,動態(tài)修改系統(tǒng)中的 Class 類型。其中,使用該軟件包的一個關(guān)鍵組件就是 Javaagent。從名字上看,似乎是個 Java 代理之類的,而實際上,他的功能更像是一個Class 類型的轉(zhuǎn)換器,他可以在運行時接受重新外部請求,對Class類型進行修改。

      Instrumentation的底層實現(xiàn)依賴于JVMTI。

      JVM 會優(yōu)先加載 帶 Instrumentation 簽名的方法,加載成功忽略第二種,如果第一種沒有,則加載第二種方法。

      Instrumentation支持的接口:

      public interface Instrumentation {
          //添加一個ClassFileTransformer
          //之后類加載時都會經(jīng)過這個ClassFileTransformer轉(zhuǎn)換
          void addTransformer(ClassFileTransformer transformer, boolean canRetransform);
      
          void addTransformer(ClassFileTransformer transformer);
          //移除ClassFileTransformer
          boolean removeTransformer(ClassFileTransformer transformer);
      
          boolean isRetransformClassesSupported();
          //將一些已經(jīng)加載過的類重新拿出來經(jīng)過注冊好的ClassFileTransformer轉(zhuǎn)換
          //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的局限性:

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

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

      http://www.rzrgm.cn/rickiyang/p/11368932.html

      再談Attach和Agent

      上面的實戰(zhàn)章節(jié)中已經(jīng)提到了attach和agent兩者的區(qū)別,這里再展開聊聊。

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

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

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

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

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

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

      這兩種方式各有不同用途,一般來說,Attach方式適合于動態(tài)的對代碼進行功能修改,在排查問題的時候用的比較多。而Agent模式隨著應(yīng)用啟動,所以經(jīng)常用于提前實現(xiàn)一些增強功能,比如我上面實戰(zhàn)中的啟動觀測,應(yīng)用防火墻,限流策略等等。

      總結(jié)

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

      筆者在公司內(nèi)部也通過它實現(xiàn)了很多小工具,比如上面的應(yīng)用啟動數(shù)據(jù)觀測(公司內(nèi)部是一個更為穩(wěn)定復(fù)雜的版本,還監(jiān)控了大量中間件的數(shù)據(jù)),幫助了很多部門同事,優(yōu)化他們應(yīng)用的啟動速度。所以如果對JVM感興趣,不妨大開腦洞,想一想JVM Sandbox還能在哪里幫助到你的工作,給自己的工作添彩。

      參考

      https://www.infoq.cn/article/tsy4lgjvsfweuxebw*gp

      http://www.rzrgm.cn/rickiyang/p/11368932.html

      https://www.jianshu.com/p/eff047d4480a

      posted @ 2022-11-14 20:22  蠻三刀醬  閱讀(4400)  評論(1)    收藏  舉報
      主站蜘蛛池模板: 亚洲码与欧洲码区别入口| 日日橹狠狠爱欧美视频| 国产一区国产精品自拍| 免费观看全黄做爰大片| 亚洲一区二区三区自拍高清 | 国产人与禽zoz0性伦多活几年| 国产普通话对白刺激| 国产乱弄免费视频观看| 日韩av熟女人妻一区二| 色欲aⅴ亚洲情无码av蜜桃| 欧美日本精品一本二本三区| 亚洲一区二区三区自拍天堂| 亚欧洲乱码视频在线专区| 草草浮力影院| 99久久99这里只有免费费精品| 少妇又爽又刺激视频| 亚洲旡码欧美大片| 无线乱码一二三区免费看| 欧美国产精品啪啪| 久久精品女人天堂av免费观看| 国产欧美久久一区二区三区| 精品偷自拍另类精品在线| 日产国产一区二区不卡| 国产午夜亚洲精品不卡网站| 四虎国产精品成人免费久久| 国产精品国产精品偷麻豆| 9久9久热精品视频在线观看| 周宁县| 亚洲春色在线视频| 激情国产一区二区三区四区| 国产精品 欧美激情 在线播放| 免费看一区无码无a片www| 少妇人妻偷人精品免费| 国产精品一区二区三区色| 国产一级av在线播放| 羞羞影院午夜男女爽爽免费视频| 亚洲gv天堂无码男同在线观看| 国产精品免费AⅤ片在线观看 | 国内揄拍国内精品人妻久久| 妓院一钑片免看黄大片| 亚洲一区二区美女av|