欧美bbbwbbbw肥妇,免费乱码人妻系列日韩,一级黄片

Java?Agent?(代理)探針技術(shù)詳情

 更新時(shí)間:2022年04月27日 14:16:32   作者:CoderJie?  
這篇文章主要介紹了Java?Agent?探針技術(shù)詳情,Java?中的?Agent?技術(shù)可以讓我們無侵入性的去進(jìn)行代理,最常用于程序調(diào)試、熱部署、性能診斷分析等場景,下文更多相關(guān)資料,感興趣的小伙伴可以參考一下

前言:

Java 中的 Agent 技術(shù)可以讓我們無侵入性的去進(jìn)行代理,最常用于程序調(diào)試、熱部署、性能診斷分析等場景,現(xiàn)如今比較火熱的分布式鏈路追蹤項(xiàng)目Skywalking,就是通過探針技術(shù)去捕獲日志,將數(shù)據(jù)上報(bào)OAP觀察分析平臺(tái)。

Java Agent 技術(shù)簡介

Java Agent 直譯為 Java 代理,也常常被稱為 Java 探針技術(shù)。

Java Agent 是在 JDK1.5 引入的,是一種可以動(dòng)態(tài)修改 Java 字節(jié)碼的技術(shù)。Java 中的類編譯后形成字節(jié)碼被 JVM 執(zhí)行,在 JVM 在執(zhí)行這些字節(jié)碼之前獲取這些字節(jié)碼的信息,并且通過字節(jié)碼轉(zhuǎn)換器對這些字節(jié)碼進(jìn)行修改,以此來完成一些額外的功能。

Java Agent 是一個(gè)不能獨(dú)立運(yùn)行 jar 包,它通過依附于目標(biāo)程序的 JVM 進(jìn)程,進(jìn)行工作。啟動(dòng)時(shí)只需要在目標(biāo)程序的啟動(dòng)參數(shù)中添加-javaagent 參數(shù)添加 ClassFileTransformer 字節(jié)碼轉(zhuǎn)換器,相當(dāng)于在main方法前加了一個(gè)攔截器。

Java Agent 功能介紹

Java Agent 主要有以下功能:

  • Java Agent 能夠在加載 Java 字節(jié)碼之前攔截并對字節(jié)碼進(jìn)行修改;
  • Java Agent 能夠在 Jvm 運(yùn)行期間修改已經(jīng)加載的字節(jié)碼;

Java Agent 的應(yīng)用場景:

  • IDE 的調(diào)試功能,例如 Eclipse、IntelliJ IDEA ;
  • 熱部署功能,例如 JRebel、XRebel、spring-loaded;
  • 各種線上診斷工具,例如 Btrace、Greys,還有阿里的 Arthas;
  • 各種性能分析工具,例如 Visual VM、JConsole 等;
  • 全鏈路性能檢測工具,例如 Skywalking、Pinpoint等;

Java Agent 實(shí)現(xiàn)原理

在了解Java Agent的實(shí)現(xiàn)原理之前,需要對Java類加載機(jī)制有一個(gè)較為清晰的認(rèn)知。一種是在man方法執(zhí)行之前,通過premain來執(zhí)行,另一種是程序運(yùn)行中修改,需通過JVM中的Attach實(shí)現(xiàn),Attach的實(shí)現(xiàn)原理是基于JVMTI。

主要是在類加載之前,進(jìn)行攔截,對字節(jié)碼修改

下面我們分別介紹一下這些關(guān)鍵術(shù)語:

  • JVMTI 就是JVM Tool Interface,是 JVM 暴露出來給用戶擴(kuò)展使用的接口集合,JVMTI 是基于事件驅(qū)動(dòng)的,JVM每執(zhí)行一定的邏輯就會(huì)觸發(fā)一些事件的回調(diào)接口,通過這些回調(diào)接口,用戶可以自行擴(kuò)展

JVMTI是實(shí)現(xiàn) Debugger、Profiler、Monitor、Thread Analyser 等工具的統(tǒng)一基礎(chǔ),在主流 Java 虛擬機(jī)中都有實(shí)現(xiàn)

  • JVMTIAgent是一個(gè)動(dòng)態(tài)庫,利用JVMTI暴露出來的一些接口來干一些我們想做、但是正常情況下又做不到的事情,不過為了和普通的動(dòng)態(tài)庫進(jìn)行區(qū)分,它一般會(huì)實(shí)現(xiàn)如下的一個(gè)或者多個(gè)函數(shù):
    • Agent_OnLoad函數(shù),如果agent是在啟動(dòng)時(shí)加載的,通過JVM參數(shù)設(shè)置
    • Agent_OnAttach函數(shù),如果agent不是在啟動(dòng)時(shí)加載的,而是我們先attach到目標(biāo)進(jìn)程上,然后給對應(yīng)的目標(biāo)進(jìn)程發(fā)送load命令來加載,則在加載過程中會(huì)調(diào)用Agent_OnAttach函數(shù)
    • Agent_OnUnload函數(shù),在agent卸載時(shí)調(diào)用
  • javaagent 依賴于instrument的JVMTIAgent(Linux下對應(yīng)的動(dòng)態(tài)庫是libinstrument.so),還有個(gè)別名叫JPLISAgent(Java Programming Language Instrumentation Services Agent),專門為Java語言編寫的插樁服務(wù)提供支持的
  • instrument 實(shí)現(xiàn)了Agent_OnLoad和Agent_OnAttach兩方法,也就是說在使用時(shí),agent既可以在啟動(dòng)時(shí)加載,也可以在運(yùn)行時(shí)動(dòng)態(tài)加載。其中啟動(dòng)時(shí)加載還可以通過類似-javaagent:jar包路徑的方式來間接加載instrument agent,運(yùn)行時(shí)動(dòng)態(tài)加載依賴的是JVM的attach機(jī)制,通過發(fā)送load命令來加載agent
  • JVM Attach 是指 JVM 提供的一種進(jìn)程間通信的功能,能讓一個(gè)進(jìn)程傳命令給另一個(gè)進(jìn)程,并進(jìn)行一些內(nèi)部的操作,比如進(jìn)行線程 dump,那么就需要執(zhí)行 jstack 進(jìn)行,然后把 pid 等參數(shù)傳遞給需要 dump 的線程來執(zhí)行

Java Agent 案例

我們就以打印方法的執(zhí)行時(shí)間為例,通過Java Agent 來實(shí)現(xiàn)。

首先我們需要構(gòu)建一個(gè)精簡的Maven項(xiàng)目,在其中構(gòu)建兩個(gè)Maven的子項(xiàng)目,一個(gè)用于實(shí)現(xiàn)外掛的Agent,一個(gè)用于實(shí)現(xiàn)測試目標(biāo)程序。

我們在父應(yīng)用中導(dǎo)入兩個(gè)項(xiàng)目公共依賴的包

    <dependencies>
        <dependency>
            <groupId>org.javassist</groupId>
            <artifactId>javassist</artifactId>
            <version>3.28.0-GA</version>
        </dependency>
    </dependencies>

首先我們?nèi)?gòu)建測試的目標(biāo)程序

// 啟動(dòng)類
public class APPMain {
    public static void main(String[] args) {
        System.out.println("APP 啟動(dòng)?。?!");
        AppInit.init();
    }
}
// 模擬的應(yīng)用初始化的類
public class AppInit {
    public static void init() {
        try {
            System.out.println("APP初始化中...");
            Thread.sleep(1000);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }
}

然后我們啟動(dòng)程序,測試是否能正常執(zhí)行,程序正常執(zhí)行之后,我們開始構(gòu)建探針程序

探針程序中我們需要編寫,改變原有class的Transformer,通過自定義的Transformer類完成輸出方法執(zhí)行時(shí)間的功能,

首先構(gòu)檢Agent程序的入口

public class RunTimeAgent {
    public static void premain(String arg, Instrumentation instrumentation) {
        System.out.println("探針啟動(dòng)?。?!");
        System.out.println("探針傳入?yún)?shù):" + arg);
        instrumentation.addTransformer(new RunTimeTransformer());
    }
}

這里每個(gè)類加載的時(shí)候都會(huì)走這個(gè)方法,我們可以通過className進(jìn)行指定類的攔截,然后借助javassist這個(gè)工具,進(jìn)行對Class的處理,這里的思想和反射類似,但是要比反射功能更加強(qiáng)大,可以動(dòng)態(tài)修改字節(jié)碼。

javassist是一個(gè)開源的分析、編輯和創(chuàng)建Java字節(jié)碼的類庫。

import javassist.ClassPool;
import javassist.CtClass;
import javassist.CtMethod;
import java.lang.instrument.ClassFileTransformer;
import java.lang.instrument.IllegalClassFormatException;
import java.security.ProtectionDomain;
public class RunTimeTransformer implements ClassFileTransformer {
    private static final String INJECTED_CLASS = "com.zhj.test.init.AppInit";
    @Override
    public byte[] transform(ClassLoader loader, String className, Class<?> classBeingRedefined, ProtectionDomain protectionDomain, byte[] classfileBuffer) throws IllegalClassFormatException {
        String realClassName = className.replace("/", ".");
        if (realClassName.equals(INJECTED_CLASS)) {
            System.out.println("攔截到的類名:" + realClassName);
            CtClass ctClass;
            try {
                // 使用javassist,獲取字節(jié)碼類
                ClassPool classPool = ClassPool.getDefault();
                ctClass = classPool.get(realClassName);
                // 得到該類所有的方法實(shí)例,也可選擇方法,進(jìn)行增強(qiáng)
                CtMethod[] declaredMethods = ctClass.getDeclaredMethods();
                for (CtMethod method : declaredMethods) {
                    System.out.println(method.getName() + "方法被攔截");
                    method.addLocalVariable("time", CtClass.longType);
                    method.insertBefore("System.out.println(\"---開始執(zhí)行---\");");
                    method.insertBefore("time = System.currentTimeMillis();");
                    method.insertAfter("System.out.println(\"---結(jié)束執(zhí)行---\");");
                    method.insertAfter("System.out.println(\"運(yùn)行耗時(shí): \" + (System.currentTimeMillis() - time));");
                }
                return ctClass.toBytecode();
            } catch (Throwable e) { //這里要用Throwable,不要用Exception
                System.out.println(e.getMessage());
                e.printStackTrace();
            }
        }
        return classfileBuffer;
    }
}

我們需要在Maven中配置,編譯打包的插件,這樣我們就可以很輕松的借助Maven生成Agent的jar包

<build>
        <plugins>
            <plugin>
                <groupId>org.apache.maven.plugins</groupId>
                <artifactId>maven-compiler-plugin</artifactId>
                <version>3.5.1</version>
                <!-- 指定maven編譯的jdk版本。若不指定,maven3默認(rèn)用jdk 1.5 maven2默認(rèn)用jdk1.3 -->
                <configuration>
                    <source>8</source>
                    <target>8</target>
                </configuration>
            </plugin>
            <plugin>
                <groupId>org.apache.maven.plugins</groupId>
                <artifactId>maven-jar-plugin</artifactId>
                <version>3.2.0</version>
                <configuration>
                    <archive>
                        <!--自動(dòng)添加META-INF/MANIFEST.MF -->
                        <manifest>
                            <addClasspath>true</addClasspath>
                        </manifest>
                        <manifestEntries>
                            <Menifest-Version>1.0</Menifest-Version>
                            <Premain-Class>com.zhj.agent.RunTimeAgent</Premain-Class>
                            <Can-Redefine-Classes>true</Can-Redefine-Classes>
                            <Can-Retransform-Classes>true</Can-Retransform-Classes>
                        </manifestEntries>
                    </archive>
                </configuration>
            </plugin>
        </plugins>
    </build>

否則我們需要在resources下創(chuàng)建META-INF/MANIFEST.MF文件,文件內(nèi)容如下,我們可以看出這個(gè)與Maven中的配置是一致的,然后通過配置編譯器,借助編譯器打包成jar包,需指定該文件

Manifest-Version: 1.0
Premain-Class: com.zhj.agent.RunTimeAgent
Can-Redefine-Classes: true
Can-Retransform-Classes: true

告示文件MANIFEST.MF參數(shù)說明:

Manifest-Version

文件版本

Premain-Class

包含 premain 方法的類(類的全路徑名)main方法運(yùn)行前代理

Agent-Class

包含 agentmain 方法的類(類的全路徑名)main開始后可以修改類結(jié)構(gòu)

Boot-Class-Path

設(shè)置引導(dǎo)類加載器搜索的路徑列表。查找類的特定于平臺(tái)的機(jī)制失敗后,引導(dǎo)類加載器會(huì)搜索這些路徑。按列出的順序搜索路徑。列表中的路徑由一個(gè)或多個(gè)空格分開。(可選)

Can-Redefine-Classes true

表示能重定義此代理所需的類,默認(rèn)值為 false(可選)

Can-Retransform-Classes true

表示能重轉(zhuǎn)換此代理所需的類,默認(rèn)值為 false (可選)

Can-Set-Native-Method-Prefix true

表示能設(shè)置此代理所需的本機(jī)方法前綴,默認(rèn)值為 false(可選)

最后通過Maven生成Agent的jar包,然后修改測試目標(biāo)程序的啟動(dòng)器,添加JVM參數(shù)即可

參數(shù)示例:-javaagent:F:\code\myCode\agent-test\runtime-agent\target\runtime-agent-1.0-SNAPSHOT.jar=hello

最終效果:

這樣就完成了無侵入的代理。

到此這篇關(guān)于Java Agent (代理)探針技術(shù)詳情的文章就介紹到這了,更多相關(guān)Java Agent 內(nèi)容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!

相關(guān)文章

  • Java中的反射機(jī)制基本運(yùn)用詳解

    Java中的反射機(jī)制基本運(yùn)用詳解

    這篇文章主要介紹了Java 反射機(jī)制原理與用法,結(jié)合實(shí)例形式詳細(xì)分析了Java反射機(jī)制的相關(guān)概念、原理、基本使用方法及操作注意事項(xiàng),需要的朋友可以參考下
    2021-08-08
  • 一種求正整數(shù)冪的高效算法詳解

    一種求正整數(shù)冪的高效算法詳解

    本篇文章是對java中一種求正整數(shù)冪的高效算法進(jìn)行了詳細(xì)的分析介紹,需要的朋友參考下
    2013-06-06
  • IDEA如何修改配置文件的存放位置

    IDEA如何修改配置文件的存放位置

    這篇文章主要介紹了IDEA如何修改配置文件的存放位置,具有很好的參考價(jià)值,希望對大家有所幫助。如有錯(cuò)誤或未考慮完全的地方,望不吝賜教
    2021-12-12
  • 關(guān)于MyBatis 查詢數(shù)據(jù)時(shí)屬性中多對一的問題(多條數(shù)據(jù)對應(yīng)一條數(shù)據(jù))

    關(guān)于MyBatis 查詢數(shù)據(jù)時(shí)屬性中多對一的問題(多條數(shù)據(jù)對應(yīng)一條數(shù)據(jù))

    這篇文章主要介紹了MyBatis 查詢數(shù)據(jù)時(shí)屬性中多對一的問題(多條數(shù)據(jù)對應(yīng)一條數(shù)據(jù)),本文給大家介紹的非常詳細(xì),對大家的學(xué)習(xí)或工作具有一定的參考借鑒價(jià)值,需要的朋友可以參考下
    2021-01-01
  • Java中ArrayList實(shí)現(xiàn)原理及基本方法

    Java中ArrayList實(shí)現(xiàn)原理及基本方法

    這篇文章主要介紹了Java中ArrayList實(shí)現(xiàn)原理及基本方法,ArrayList是開發(fā)中非常常用的數(shù)據(jù)存儲(chǔ)容器之一,其底層是數(shù)組實(shí)現(xiàn)的,我們可以在集合中存儲(chǔ)任意類型的數(shù)據(jù),ArrayList是線程不安全的,擅長隨機(jī)訪問元素,插入和刪除較慢,需要的朋友可以參考下
    2023-08-08
  • SpringBoot整合Elasticsearch游標(biāo)查詢的示例代碼(scroll)

    SpringBoot整合Elasticsearch游標(biāo)查詢的示例代碼(scroll)

    這篇文章主要介紹了SpringBoot整合Elasticsearch游標(biāo)查詢(scroll),本文給大家介紹的非常詳細(xì),對大家的學(xué)習(xí)或工作具有一定的參考借鑒價(jià)值,需要的朋友可以參考下
    2020-10-10
  • java設(shè)計(jì)模式—靜態(tài)代理模式(聚合與繼承方式對比)

    java設(shè)計(jì)模式—靜態(tài)代理模式(聚合與繼承方式對比)

    下面小編就為大家?guī)硪黄猨ava設(shè)計(jì)模式—靜態(tài)代理模式(聚合與繼承方式對比)。小編覺得挺不錯(cuò)的,現(xiàn)在就分享給大家,也給大家做個(gè)參考。一起跟隨小編過來看看吧
    2017-05-05
  • 使用Spring框架實(shí)現(xiàn)用戶登錄

    使用Spring框架實(shí)現(xiàn)用戶登錄

    這篇文章主要為大家詳細(xì)介紹了使用Spring框架實(shí)現(xiàn)用戶登錄,文中示例代碼介紹的非常詳細(xì),具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下
    2022-09-09
  • 基于spring-boot-maven-plugin插件打包lib文件外置的方法(layout模式為ZIP模式)

    基于spring-boot-maven-plugin插件打包lib文件外置的方法(layout模式為ZIP模式)

    Maven是一個(gè)插件執(zhí)行框架,所有工作都由插件完成,同時(shí)?Maven?基于構(gòu)建生命周期的核心概念,明確定義了構(gòu)建和分發(fā)特定工件(項(xiàng)目)的過程,接下來通過本文給大家介紹下基于spring-boot-maven-plugin插件打包lib文件外置(layout模式為ZIP模式),需要的朋友可以參考下
    2022-09-09
  • 如何處理maven倉庫中后綴LastUpdated文件

    如何處理maven倉庫中后綴LastUpdated文件

    這篇文章主要介紹了如何處理maven倉庫中后綴LastUpdated文件,文中通過示例代碼介紹的非常詳細(xì),對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友可以參考下
    2020-04-04

最新評論