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

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

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

      JVM Sandbox入門詳解

      一. 概述

      在日常開發中,經常會接觸到面向AOP編程的思想,我們通常會使用Spring AOP來做統一的權限認證、異常捕獲返回、日志記錄等工作。之所以使用Spring AOP來實現上述功能,是因為這些場景本質上來說都是與業務場景掛鉤的,但是具有一定的抽象程度,并且絕大多數業務邏輯類都已經被Spring容器托管了。但是這個世界上不是所有的Java應用都接入了Spring框架,接入Spring的應用也不是所有類都會被Spring容器托管,例如很多中間件代碼、三方包代碼,Java原生代碼,都不能被Spring AOP代理到,所以在很多場景下Spring AOP都無法滿足AOP編碼的需求。

      上面是從技術實現出發,說明了Spring AOP的局限性。如果從領域職責出發,像應用指標監控,全鏈路監控,故障定位,流量回放等與業務無關的場景代碼放在業務系統中實現顯然并不合適,此時如果有一種更為通用的AOP方式,將通用邏輯與業務邏輯解耦,豈不是美哉。

      《JavaAgent詳解》 中,我們提到了Java自身提供了JVM Instrumentation等功能,允許使用者以通過一系列API完成對JVM的復雜控制,以及字節碼增強。但是如果使用原生的Java Agent能力,從技術實現上來說沒有太大問題(畢竟越底層的技術越靈活,能夠實現的功能越多),但是實現成本和門檻都比較高,開發者需要小心翼翼的操作目標類的字節碼,在想要監控的目標方法前后插入對應的業務邏輯。雖然市面上有很多類似于 Byte Buddy 字節碼增強庫,大大簡化了字節碼增強的復雜度,但是開發成本和技術門檻都相對很高。而由阿里巴巴開源的 jvm-sandbox 在 Java Instrumentation API 的基礎上實現了運行時 AOP 增強的能力,開發者不需要去操作目標類的字節碼,即可對目標類進行字節碼增強。

      是不是看到這里還是不清楚我在講什么?別急,我舉幾個典型的 jvm-sandbox 應用場景:

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

      可以看到,借助jvm-sandbox,你可以實現很多之前在業務代碼中做不了的事,大大拓展了可操作的范圍。

      二. 整體架構

      本章節不詳細講述JVM SandBox的所有架構設計,只講其中幾個最重要的特性。詳細的架構設計可以看原框架代碼倉庫的Wiki。

      2.1 類隔離

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

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

      2.2 無侵入AOP與事件驅動

      在常見的AOP框架實現方案中,有靜態編織和動態編織兩種。

      1. 靜態編織:靜態編織發生在字節碼生成時根據一定框架的規則提前將AOP字節碼插入到目標類和方法中,實現AOP;

      2. 動態編織

        :動態編織則允許在JVM運行過程中完成指定方法的AOP字節碼增強.常見的動態編織方案大多采用重命名原有方法,再新建一個同簽名的方法來做代理的工作模式來完成AOP的功能(常見的實現方案如CgLib),但這種方式存在一些應用邊界:

        • 侵入性:對被代理的目標類需要進行侵入式改造。比如:在Spring中必須是托管于Spring容器中的Bean
        • 固化性:目標代理方法在啟動之后即固化,無法重新對一個已有方法進行AOP增強

      要解決無侵入的特性需要AOP框架具備 在運行時完成目標方法的增強和替換。在JDK的規范中運行期重定義一個類必須準循以下原則

      1. 不允許新增、修改和刪除成員變量
      2. 不允許新增和刪除方法
      3. 不允許修改方法簽名

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

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

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

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

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

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

      三. 代碼實踐

      我們還是以 《JavaAgent詳解》 中的案例為例,首先定義 Person 類:

      package cn.bigcoder.demo.agenttest;
      
      import java.util.Random;
      
      public class Person {
         public String test() {
             System.out.println("執行測試方法");
             try {
                 Thread.sleep(new Random().nextInt(1000));
             } catch (InterruptedException e) {
                 throw new RuntimeException(e);
             }
             return "I'm ok";
        }
      }
      

      該類中有一個 test 方法,會在執行時隨機停頓一段時間,模擬業務代碼中的無法預測的執行時間,定義Main方法不斷調用 Person.test 方法,模擬不間斷的業務請求:

      package cn.bigcoder.demo.agenttest;
      
      import java.util.Scanner;
      
      /**
       * @author: bigcoder
       **/
      public class AgentTest {
      
          public static void main(String[] args) {
              while (true) {
                  Person person = new Person();
                  person.test();
              }
          }
      }
      

      我們通過jvm-sandbox實現 Person.test 方法執行耗時的監控打印。

      3.1 新建sandbox模塊工程

      首先新建Maven工程,假設用的是MAVEN,這里通過將parent指向sandbox-module-starter來簡化我們的配置工作

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

      3.2 編寫模塊代碼

      package cn.bigcoder.demo.sandbox;
      
      import com.alibaba.jvm.sandbox.api.Information;
      import com.alibaba.jvm.sandbox.api.Module;
      import com.alibaba.jvm.sandbox.api.ModuleLifecycle;
      import com.alibaba.jvm.sandbox.api.annotation.Command;
      import com.alibaba.jvm.sandbox.api.listener.ext.Advice;
      import com.alibaba.jvm.sandbox.api.listener.ext.AdviceListener;
      import com.alibaba.jvm.sandbox.api.listener.ext.EventWatchBuilder;
      import com.alibaba.jvm.sandbox.api.resource.ModuleEventWatcher;
      import org.kohsuke.MetaInfServices;
      
      import javax.annotation.Resource;
      
      /**
       * @author: Jindong.Tian
       * @date: 2023-05-27
       **/
      @MetaInfServices(Module.class)
      @Information(id = "person-test-monitor")
      public class PersonTimeMonitorModule implements Module {
          @Resource
          private ModuleEventWatcher moduleEventWatcher;
      
          @Command("monitorExecuteTime")
          public void monitorExecuteTime() {
      
              new EventWatchBuilder(moduleEventWatcher)
                      // 增強 cn.bigcoder.demo.agenttest.Person 類
                      .onClass("cn.bigcoder.demo.agenttest.Person")
                      // 增強 cn.bigcoder.demo.agenttest.Person 類的test方法
                      .onBehavior("test")
                      .onWatch(new AdviceListener() {
                          @Override
                          protected void before(Advice advice) throws Throwable {
                              // 獲取執行開始時間
                              advice.attach(System.currentTimeMillis());
                          }
      
                          @Override
                          public void afterReturning(Advice advice) throws Throwable {
                              // 在方法調用后計算方法耗時,并打印出來
                              long startTime = (long) advice.attachment();
                              long endTime = System.currentTimeMillis();
                              String className = advice.getBehavior().getDeclaringClass().getName();
                              String methodName = advice.getBehavior().getName();
                              System.out.println(className + "." + methodName + " executed in " + (endTime - startTime) + " ms");
                          }
                      });
          }
      
      }
      
      

      3.3 Maven構建

      $ mvn clean package
      

      3.4 下載并安裝沙箱

      下載并安裝最新版本沙箱:

      • 下載地址:https://ompc.oss.aliyuncs.com/jvm-sandbox/release/sandbox-stable-bin.zip

      • 執行安裝

        unzip sandbox-stable-bin.zip
        cd sandbox
        

      將打好的包復制到用戶模塊目錄下:

      cp ./jvm-sandbox-demo/target/jvm-sandbox-demo-1.0-SNAPSHOT.jar /home/bigcoder/.opt/sandbox
      

      3.5 啟動目標類

      啟動 AgentTest.main,此時代碼一直輸出:

      執行測試方法
      執行測試方法
      執行測試方法
      執行測試方法
      ...
      

      3.6 啟動沙箱,并激活模塊

      ?  sandbox jps -l
      2241 cn.bigcoder.demo.agenttest.AgentTest
      2264 jdk.jcmd/sun.tools.jps.Jps
      ?  sandbox ./bin/sandbox.sh -p 2241 -d 'person-test-monitor/monitorExecuteTime' 
      

      使用 jps 命令查看目標進程ID,然后啟動沙箱,-p 參數指定目標進程 pid-d 參數指定要激活的的模塊。

      此時,我們可以看到控制臺開始打印方法執行耗時了:

      3.7 卸載沙箱

      ?  sandbox ./bin/sandbox.sh -p 2241 -S
      jvm-sandbox[default] shutdown finished.
      

      此時,控制臺又恢復了往日的平靜,不再輸出方法耗時:

      3.8 agent方式增強

      在 3.6 中我們是以attch方式增強已運行的JVM進程,有些時候我們需要沙箱工作在應用代碼加載之前,或者一次性渲染大量的類、加載大量的模塊,此時如果用ATTACH方式加載,可能會引起目標JVM的卡頓或停頓(GC),這就需要用到AGENT的啟動方式。

      假設SANDBOX被安裝在了/homt/bigcoder/.opt/sandbox,需要在JVM啟動參數中增加上 -javaagent:/homt/bigcoder/.opt/sandbox/lib/sandbox-agent.jar

      四. 模塊的生命周期

      模塊生命周期類型有模塊加載模塊卸載模塊激活模塊凍結模塊加載完成五個狀態。

      • 模塊加載:創建ClassLoader,完成模塊的加載
      • 模塊卸載:模塊增強的類會重新load,去掉增強的字節碼
      • 模塊激活:模塊被激活后,模塊所增強的類將會被激活,所有com.alibaba.jvm.sandbox.api.listener.EventListener將開始收到對應的事件
      • 模塊凍結:模塊被凍結后,模塊所持有的所有com.alibaba.jvm.sandbox.api.listener.EventListener將被靜默,無法收到對應的事件。需要注意的是,模塊凍結后雖然不再收到相關事件,但沙箱給對應類織入的增強代碼仍然還在。
      • 模塊加載完成:模塊加載已經完成,這個狀態是為了做日志處理,本身不會影響模塊變更行為

      模塊可以通過實現com.alibaba.jvm.sandbox.api.ModuleLifecycle接口,對模塊生命周期進行控制,接口中的方法:

      • onLoad:模塊開始加載之前調用
      • onUnload:模塊開始卸載之前調用
      • onActive:模塊被激活之前調用,拋出異常將會是阻止模塊被激活的唯一方式
      • onFrozen:模塊被凍結之前調用,拋出異常將會是阻止模塊被凍結的唯一方式

      五. 小結

      本文從 Spring AOP 的局限性出發,探討了 jvm-sandbox 使用場景。如果使用 Java Instrumentation API 增強目標方法,理論上來說也能實現目標類增強,但是字節碼修改的門檻過高。而 jvm-sandbox 提供了運行時AOP增強的能力,雖然底層仍然基于 Java Instrumentation API,但是程序員不再需要關心字節碼增強的細節,就能完成對原有方法的邏輯增強。

      我們引入了 《JavaAgent詳解》 文章中相同的例子,講解了如何使用 jvm-sandbox 去增強 Person.test 方法,從而統計方法執行耗時。該例只是一個簡單的入門案例,如果對jvm-sandbox 感興趣,可以學習官方沙箱分發包中自帶的實用工具的例子./example/sandbox-debug-module.jar,代碼在沙箱的sandbox-debug-module模塊:

      例子例子說明
      DebugWatchModule.java模仿GREYS的watch命令
      DebugTraceModule.java模仿GREYES的trace命令
      DebugRalphModule.java無敵破壞王,故障注入(延時、熔斷、并發限流、TPS限流)
      LogExceptionModule.java記錄下你的應用都發生了哪些異常 $HOME/logs/sandbox/debug/exception-monitor.log
      LogServletAccessModule.java記錄下你的應用的HTTP服務請求 $HOME/logs/sandbox/debug/servlet-access.log

      本文參考至:

      JVM Sandbox入門教程與原理淺談 - 蠻三刀醬 - 博客園 (cnblogs.com)

      Home · alibaba/jvm-sandbox Wiki (github.com)

      posted @ 2023-05-27 21:36  聽到微笑  閱讀(187)  評論(0)    收藏  舉報  來源
      主站蜘蛛池模板: 国产午夜精品福利视频| 内射老阿姨1区2区3区4区| 亚洲性日韩精品一区二区三区| 日本中文一二区有码在线| 激情无码人妻又粗又大| 偷拍一区二区三区在线视频| 蜜桃网址| 综合图区亚洲另类偷窥| 亚洲乱理伦片在线观看中字| 亚洲欧洲日韩国内高清| 在线视频一区二区三区色| 精品久久久久久久久午夜福利| 毛多水多高潮高清视频| 色伦专区97中文字幕| 么公的好大好硬好深好爽视频| 亚洲 欧洲 无码 在线观看| 日韩在线视频线观看一区| 亚洲激情一区二区三区在线| 疯狂做受xxxx高潮欧美日本 | 亚洲精品毛片一区二区| 国产又黄又爽又不遮挡视频| av资源在线看免费观看| 久久天天躁狠狠躁夜夜avapp| 国产一区二区日韩在线| 亚洲另类激情专区小说图片| 国产综合色一区二区三区| 国产福利萌白酱在线观看视频| 亚洲日产韩国一二三四区| 精品无码久久久久久久久久| 精品蜜臀国产av一区二区| 69人妻精品中文字幕| 亚洲爆乳少妇无码激情| 五月天国产成人AV免费观看| 男人猛躁进女人免费播放| 国产中文字幕在线精品| 精品国产一区二区三区av性色 | 久久国产成人午夜av影院| 国产精品亚洲二区在线看| 伊大人香蕉久久网欧美| 亚洲一区av在线观看| 台湾佬自拍偷区亚洲综合|