日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問 生活随笔!

生活随笔

當(dāng)前位置: 首頁 > 编程语言 > java >内容正文

java

如何脚踏实地构建Java Agent

發(fā)布時間:2023/12/3 java 49 豆豆
生活随笔 收集整理的這篇文章主要介紹了 如何脚踏实地构建Java Agent 小編覺得挺不錯的,現(xiàn)在分享給大家,幫大家做個參考.

在構(gòu)建Plumbr的多年中,我們遇到了許多具有挑戰(zhàn)性的問題。 在其他方面,使Plumbr Java Agent可靠地執(zhí)行而又不危害客戶的應(yīng)用程序,是一個特別棘手的任務(wù)。 從實(shí)時系統(tǒng)中安全地收集所有需要的遙測會帶來很多問題。 其中一些非常簡單,而另一些則非常不明顯。

在此博客文章中,我們想與您分享一些示例,這些示例演示了在為我們的探員需要處理的一些看似簡單的方面提供支持時遇到的復(fù)雜性。 這些示例進(jìn)行了一些簡化,但摘錄自我們前一段時間需要解決的現(xiàn)實(shí)問題。 實(shí)際上,這些只是等待嘗試使用字節(jié)碼工具或JVMTI的人的冰山一角。

示例1:檢測一個簡單的Web應(yīng)用程序

讓我們從一個非常簡單的hello world網(wǎng)絡(luò)應(yīng)用開始 :

@Controller public class HelloWorldController {@RequestMapping("/hello")@ResponseBodyString hello() {return "Hello, world!";} }

如果啟動應(yīng)用程序并訪問相關(guān)的控制器,則會看到以下內(nèi)容:

$ curl localhost:8080/hello Hello, world!

作為簡單的練習(xí),讓我們將返回值更改為“ Hello,transformed world”。 自然,我們真正的Java代理不會對您的應(yīng)用程序執(zhí)行此類操作:我們的目標(biāo)是在不更改觀察到的行為的情況下進(jìn)行監(jiān)視。 但是為了使這個演示簡短而簡潔,請耐心等待。 要更改返回的響應(yīng),我們將使用ByteBuddy :

public class ServletAgent {public static void premain(String arguments, Instrumentation instrumentation) { // (1)new AgentBuilder.Default().type(isSubTypeOf(Servlet.class)) // (2).transform((/* … */) ->builder.method(named("service")) // (3).intercept(MethodDelegation.to(Interceptor.class) // (4))).installOn(instrumentation); // (5)}}

這里發(fā)生了什么事:

  • 正如典型的Java代理一樣,我們提供了pre-main方法。 這將在實(shí)際應(yīng)用程序啟動之前執(zhí)行。 如果您想了解更多信息,ZeroTurnaround上有一篇很好的文章,提供了有關(guān)檢測Java代理如何工作的更多信息。
  • 我們發(fā)現(xiàn)所有類都是Servlet類的子類。 Spring的魔力最終也將出現(xiàn)在Servlet中。
  • 我們找到一種名為“服務(wù)”的方法
  • 我們攔截對該方法的調(diào)用,并將其委托給我們的自定義攔截器,該攔截器僅顯示“ Hello,transformed world!”。 到ServletOutputStream。
  • 最后,我們告訴ByteBuddy根據(jù)上述規(guī)則對裝入JVM的類進(jìn)行檢測
  • las,如果我們嘗試運(yùn)行此命令,則應(yīng)用程序?qū)⒉辉賳?#xff0c;并引發(fā)以下錯誤:

    java.lang.NoSuchMethodError: javax.servlet.ServletContext.getVirtualServerName()Ljava/lang/String;at org.apache.catalina.authenticator.AuthenticatorBase.startInternal(AuthenticatorBase.java:1137)at org.apache.catalina.util.LifecycleBase.start(LifecycleBase.java:150)

    發(fā)生了什么? 我們只觸摸了“ Servlet”類上的“ service”方法,但是現(xiàn)在JVM無法在另一個類上找到另一個方法。 腥。 讓我們嘗試看看在這兩種情況下該類的加載位置。 為此,我們可以將-XX:+ TraceClassLoading參數(shù)添加到JVM啟動腳本中。 如果沒有Java代理,則從Tomcat加載有問題的類:

    [Loaded javax.servlet.ServletContext from jar:file:app.jar!/BOOT-INF/lib/tomcat-embed-core-8.5.11.jar!/]

    但是,如果再次啟用Java代理,則會從其他位置加載它:

    [Loaded javax.servlet.ServletContext from file:agent.jar]

    啊哈! 實(shí)際上,我們的代理直接依賴于Gradle構(gòu)建腳本中定義的servlet API:

    agentCompile "javax.servlet:servlet-api:2.5"

    可悲的是,該版本與Tomcat期望的版本不匹配,因此出現(xiàn)錯誤。 我們用這種依賴性指定哪些類儀器:isSubTypeOf(Servlet ),但是這也造成了我們加載的servlet庫的不兼容版本。 要擺脫這種情況實(shí)際上并不那么容易:要檢查我們嘗試檢測的類是否是另一種類型的子類型,我們必須知道其所有父類或接口。

    盡管有關(guān)直接父代的信息存在于字節(jié)碼中,但傳遞繼承卻不存在。 實(shí)際上,在進(jìn)行檢測時,相關(guān)的類甚至可能尚未加載。 要解決此問題,我們必須在運(yùn)行時找出客戶端應(yīng)用程序的整個類層次結(jié)構(gòu)。 有效地收集類層次結(jié)構(gòu)是一項(xiàng)艱巨的任務(wù),它本身就有很多陷阱,但是這里的教訓(xùn)很明顯:規(guī)范不應(yīng)加載客戶端應(yīng)用程序可能也要加載的類,尤其是來自不兼容版本的類。

    這只是一條小小的龍,當(dāng)您嘗試使用字節(jié)碼或嘗試與類加載器混為一談時,它已遠(yuǎn)離軍團(tuán)等待著您。 我們已經(jīng)看到了許多其他問題:類加載死鎖,驗(yàn)證程序錯誤,多個代理之間的沖突,本機(jī)JVM結(jié)構(gòu)膨脹,您好!

    但是,我們的代理并不限于使用Instrumentation API。 要實(shí)現(xiàn)某些功能,我們必須更深入。

    示例2:使用JVMTI收集有關(guān)類的信息

    有多種方法可以弄清類型層次結(jié)構(gòu),但在本文中,我們僅關(guān)注其中一種-JVMTI (JVM工具接口)。 它使我們能夠編寫一些本機(jī)代碼,以訪問JVM的更底層的遙測和工具功能。 除其他外,可以為應(yīng)用程序或JVM本身中發(fā)生的各種事件訂閱JVMTI回調(diào)。 我們當(dāng)前感興趣的是ClassLoad回調(diào)。 這是一個如何使用它來訂閱類加載事件的示例 :

    static void register_class_loading_callback(jvmtiEnv* jvmti) {jvmtiEventCallbacks callbacks;jvmtiError error;memset(&callbacks, 0, sizeof(jvmtiEventCallbacks));callbacks.ClassLoad = on_class_loaded;(*jvmti)->SetEventCallbacks(jvmti, &callbacks, sizeof(callbacks));(*jvmti)->SetEventNotificationMode(jvmti, JVMTI_ENABLE, JVMTI_EVENT_CLASS_LOAD, (jthread)NULL); }

    這將使JVM在類加載的早期階段執(zhí)行我們定義的on_class_loaded函數(shù)。 然后,我們可以編寫此函數(shù),以便它通過JNI調(diào)用代理的java方法,如下所示:

    void JNICALL on_class_loaded(jvmtiEnv *jvmti, JNIEnv* jni, jthread thread, jclass klass) {(*jni)->CallVoidMethod(jni, agent_in_java, on_class_loaded_method, klass); }

    為了簡單起見,在Java Agent中,我們將只打印類的名稱:

    public static void onClassLoaded(Class clazz) {System.out.println("Hello, " + clazz); }

    閉上你的眼睛一分鐘,嘗試想象這里可能出什么問題。

    你們中許多人可能以為這將崩潰。 畢竟,您在本機(jī)代碼中犯的每個錯誤都有可能通過段錯誤使整個應(yīng)用程序崩潰。 但是,在這個特定示例中,我們實(shí)際上將獲得一些JNI錯誤和一個Java異常:

    Error: A JNI error has occurred, please check your installation and try again Error: A JNI error has occurred, please check your installation and try again Hello, class java.lang.Throwable$PrintStreamOrWriter Hello, class java.lang.Throwable$WrappedPrintStream Hello, class java.util.IdentityHashMap Hello, class java.util.IdentityHashMap$KeySet Exception in thread "main" java.lang.NullPointerExceptionAt JvmtiAgent.onClassLoaded(JvmtiAgent.java:23)

    讓我們暫時將JNI錯誤放在一邊,然后集中討論Java異常。 真令人驚訝 在這里什么可以為空? 選項(xiàng)不多,所以讓我們檢查一下并再次運(yùn)行:

    public static void onClassLoaded(Class clazz) {if(System.out == null) {throw new AssertionError("System.out is null");}if(clazz == null) {throw new AssertionError("clazz is null");}System.out.println("Hello, " + clazz); }

    但是,a,我們?nèi)匀粫龅较嗤漠惓?#xff1a;

    Exception in thread "main" java.lang.NullPointerExceptionAt JvmtiAgent.onClassLoaded(JvmtiAgent.java:31)

    讓我們稍等一下,然后對代碼進(jìn)行另一個簡單的更改:

    public static void onClassLoaded(Class clazz) {System.out.println("Hello, " + clazz.getSimpleName()); }

    輸出格式的這種看似微不足道的變化導(dǎo)致了行為上的巨大變化:

    Error: A JNI error has occurred, please check your installation and try again Error: A JNI error has occurred, please check your installation and try again Hello, WrappedPrintWriter Hello, ClassCircularityError # # A fatal error has been detected by the Java Runtime Environment: # # Internal Error (systemDictionary.cpp:806), pid=82384, tid=0x0000000000001c03 # guarantee((!class_loader.is_null())) failed: dup definition for bootstrap loader?

    啊,終于崩潰了! 真高興! 實(shí)際上,這為我們提供了很多信息,有助于查明根本原因。 具體來說,現(xiàn)在明顯的ClassCircularityError和內(nèi)部錯誤消息非常明顯。 如果要查看JVM源代碼的相關(guān)部分,您會發(fā)現(xiàn)一種用于解析類的極其復(fù)雜且混雜的算法。 它確實(shí)可以單獨(dú)工作,但仍然很脆弱,但是很容易因做一些不尋常的事情而被破壞,例如重寫ClassLoader.loadClass或拋出一些JVMTI回調(diào)。

    我們在這里所做的是將類加載潛入加載類的中間,這似乎是一項(xiàng)冒險的業(yè)務(wù)。 跳過故障排除過程,而該故障排除過程將自己撰寫一篇博客文章,涉及很多本機(jī)挖掘工作,讓我們僅概述第一個示例中發(fā)生的事情:

  • 我們嘗試加載一個類,例如launcher.LauncherHelper
  • 為了打印出來,我們嘗試加載io.PrintStream類,遞歸到相同的方法。 由于遞歸是通過JVM內(nèi)部以及JVMTI和JNI進(jìn)行的,因此在任何堆棧跟蹤中都看不到它。
  • 現(xiàn)在也必須打印出PrintStream。 但是還沒有完全加載,所以我們收到一個JNI錯誤
  • 現(xiàn)在,我們繼續(xù)嘗試?yán)^續(xù)打印。 要連接字符串,我們需要加載lang.StringBuilder。 重復(fù)同樣的故事。
  • 最后,由于類加載不多,我們得到了一個空指針異常。
  • 好吧,那很復(fù)雜。 但是畢竟,JVMTI文檔非常明確地說我們應(yīng)該格外小心:

    “此事件是在加載課程的早期階段發(fā)送的。 因此,該類應(yīng)謹(jǐn)慎使用。 請注意,例如,方法和字段尚未加載,因此對方法,字段,子類等的查詢不會給出正確的結(jié)果。 請參見Java語言規(guī)范中的“類和接口的加載”。 對于大多數(shù)目的, ClassPrepare 事件將更加有用。”

    確實(shí),如果我們使用此回調(diào),那么就不會有這樣的困難。 但是,在設(shè)計(jì)用于監(jiān)視目的的Java代理時,有時會被迫進(jìn)入JVM的非常暗的區(qū)域以支持我們所需的產(chǎn)品功能,而開銷卻足以用于生產(chǎn)部署。

    帶走

    這些示例說明了一些看似無辜的設(shè)置和天真的方法來構(gòu)建Java代理如何以令人驚訝的方式讓您大吃一驚。 實(shí)際上,以上內(nèi)容幾乎不涉及我們多年來發(fā)現(xiàn)的內(nèi)容。

    再加上數(shù)量眾多的不同平臺,此類代理將需要完美運(yùn)行(不同的JVM供應(yīng)商,不同的Java版本,不同的操作系統(tǒng)),并且本來就很復(fù)雜的任務(wù)變得更具挑戰(zhàn)性。

    但是,通過盡職調(diào)查和適當(dāng)?shù)谋O(jiān)視,構(gòu)建可靠的Java代理是一項(xiàng)可以由一組敬業(yè)工程師解決的任務(wù)。 我們在自己的產(chǎn)品中自信地運(yùn)行Plumbr Agent,并且不會因此而睡不著。

    翻譯自: https://www.javacodegeeks.com/2017/06/shoot-foot-building-java-agent.html

    總結(jié)

    以上是生活随笔為你收集整理的如何脚踏实地构建Java Agent的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。

    如果覺得生活随笔網(wǎng)站內(nèi)容還不錯,歡迎將生活随笔推薦給好友。

    主站蜘蛛池模板: 成人在线观看免费爱爱 | 国产小视频免费在线观看 | 国产a√精品区二区三区四区 | 免费成人高清在线视频 | 麻豆视频播放 | 久久久精品美女 | 免费一级黄色片 | 欧美在线精品一区二区三区 | 日本精品久久久久中文字幕 | 日韩久久久久久久久久 | 91天天射| 亚洲特黄视频 | 久久97精品| 日韩精品人妻一区二区三区免费 | 久久av一区 | 国产97免费视频 | 国产精品一区二区免费视频 | 麻豆精品国产传媒av | 亚洲最大中文字幕 | 孕妇毛片| 亚洲精品国产精品国自产观看 | 开心激情五月婷婷 | 91丨porny丨在线 | www.av在线播放| 久草午夜 | 日本国产亚洲 | 日韩av综合网站 | 亚洲视频久久久 | 色老头一区二区三区 | 久草蜜桃| 76少妇精品导航 | 国产香蕉视频 | 91精品视频免费看 | 91亚色 | 天天综合天天 | 奇米影视中文字幕 | 久久99热人妻偷产国产 | 成人毛片100部免费看 | 国产一区欧美二区 | 瑟瑟视频在线观看 | 久久av无码精品人妻系列试探 | cekc老妇女cea0| 国内精品卡一卡二卡三 | 中文字幕在线观看播放 | 国产午夜精品无码一区二区 | 综合网色 | 玖玖久久 | 国产视频69| 中文字幕欧美激情 | 黑丝一区二区三区 | 爱情岛av| 亚洲天堂avav | 中文字幕性 | 91日韩中文字幕 | 先锋资源av在线 | 欧美激情一区二区三区免费观看 | 熟妇高潮一区二区三区 | 中文字幕第 | 成人毛片视频免费看 | 羞羞答答av | 成人小说亚洲一区二区三区 | 高潮爽爆喷水h | 国产又粗又黄又爽又硬 | 男人影院在线观看 | 免费在线观看国产精品 | 伊人中文字幕在线 | av在线电影院 | 影音先锋男人站 | 中文字幕在线观看网 | 亚洲国产日韩在线观看 | 亚洲乱码一区二区 | 日本大尺度做爰呻吟舌吻 | 国产8区| 国产99久久九九精品无码免费 | 手机看片福利久久 | 免费不卡av | 婷婷综合社区 | 69堂免费视频 | 免费看v片 | 男人的天堂视频 | 久久久麻豆 | 欧美日比视频 | 国产一区二区在线免费 | 国产无限制自拍 | 一级片小视频 | 新香蕉视频 | 日韩黄色大全 | av无码av天天av天天爽 | 欧美一区二区三区免费观看 | 国产视频1区2区 | 少妇扒开粉嫩小泬视频 | 真人毛片视频 | av在线色| 亚洲永久免费网站 | 制服丝袜在线看 | 久久亚洲精品无码va白人极品 | 狠狠操狠狠操狠狠操 | 不卡av免费 | 粉嫩小泬无遮挡久久久久久 |