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

歡迎訪問(wèn) 生活随笔!

生活随笔

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

java

Java动态追踪技术--BTrace

發(fā)布時(shí)間:2023/12/4 java 39 豆豆
生活随笔 收集整理的這篇文章主要介紹了 Java动态追踪技术--BTrace 小編覺(jué)得挺不錯(cuò)的,現(xiàn)在分享給大家,幫大家做個(gè)參考.

Java動(dòng)態(tài)追蹤技術(shù)

  • 需求翻譯官的日常工作不是在的改bug,發(fā)布代碼,就是在加日志查找bug的路上。查BUG的過(guò)程是痛苦的,我們總是在不停的看代碼,修改代碼,添加日志,從而幫助我們發(fā)現(xiàn)問(wèn)題,這種形式是比較繁瑣的,不斷的在發(fā)布項(xiàng)目,開(kāi)發(fā)效率也不高,而且線上的問(wèn)題排查不可能讓你經(jīng)常添加調(diào)試代碼,能不能找到一種和JSP一樣快捷的方式呢。
JSP模式
  • 對(duì)應(yīng)大多數(shù)程序員來(lái)說(shuō),早期的時(shí)候都接觸過(guò)JSP(java server pages)。雖然之后在前后端分離的主流思想下主鍵淘汰不用了,但是JSP還是有一些比較值得我們?nèi)ニ伎嫉狞c(diǎn),例如我們?cè)谑褂肑SP寫(xiě)頁(yè)面效果的時(shí)候,修改代碼只需要刷新頁(yè)面,而不需重啟服務(wù),就可以看到頁(yè)面展示效果。并不需要重啟JVM。

  • 按照我們的常識(shí),java程序一般都需要啟動(dòng)時(shí)候加載類(lèi)文件,如果像JSP這樣修改完代碼,不用重啟就生效,那么我們就不用這么痛苦的查bug,直接加代碼又不用發(fā),加日志看下就解決了。其實(shí)是JSP的運(yùn)行機(jī)制和java不一樣,當(dāng)我們打開(kāi)瀏覽器,請(qǐng)求一個(gè)JSP文件有如下流程:

  • JSP文件修改后,之所以能及時(shí)生效,是因?yàn)閠omcate會(huì)檢查請(qǐng)求的JSP文件是否被更改過(guò)。如果發(fā)送過(guò)更改,呢么就會(huì)將JSP文件重新解析,翻譯成一個(gè)新的java,從而產(chǎn)生新的servlet類(lèi),加載到j(luò)vm中。

  • 但是此處有一個(gè)問(wèn)題,根據(jù)java類(lèi)加載機(jī)制,同一個(gè)classLoader中,類(lèi)是不允許重復(fù)的。為繞開(kāi)這個(gè)限制,Tomcate每次會(huì)創(chuàng)建一個(gè)新的ClassLoader實(shí)例,來(lái)加載新編譯的servlet類(lèi)。之后的請(qǐng)求都會(huì)有這個(gè)新的Servlet來(lái)處理,這樣就實(shí)現(xiàn)了新舊JSP的切換。

  • HTTP服務(wù)是無(wú)狀態(tài)的,所以JSP的場(chǎng)景基本上是一次性消費(fèi),請(qǐng)求后給當(dāng)前解析后的jsp內(nèi)容,這種通過(guò)ClassLoader來(lái)替換class的方法在JSP上可行,因?yàn)橛幸粋€(gè)翻譯過(guò)程,但是在Spring應(yīng)用中,對(duì)象大多是單例,存在內(nèi)存中,就算創(chuàng)建新ClassLoader也要全部在處理一次,不太現(xiàn)實(shí),所有這種方式在Spring項(xiàng)目中不可行。

  • BTrace 插樁

java 對(duì)象行為
  • java對(duì)象使用兩種東西來(lái)描述事物: 方法,屬性
    • java對(duì)象的屬性跟著對(duì)象走,每個(gè)對(duì)象存儲(chǔ)一份
    • java對(duì)象的方法,函數(shù)存儲(chǔ)在方法區(qū)
  • 如上圖:
    • 方法區(qū)中數(shù)據(jù)是類(lèi)加載時(shí)候從class文件中取出
    • class文件是從java或者其他符號(hào)jvm規(guī)范的源碼中編譯來(lái)的
    • 源碼我們我們可以自己控制
  • 通過(guò)上面幾個(gè)步驟我們能否找到辦法去修改需要加載的類(lèi),從而達(dá)到我們的目的,并且需要滿(mǎn)足幾個(gè)條件:
    • 需要修改字節(jié)碼中目標(biāo)方法所在區(qū)域,然后重新加載這個(gè)類(lèi)
    • 只修改調(diào)用方法,不修改對(duì)象的屬性,也不印象已經(jīng)存在的對(duì)象狀態(tài)
    • 不違背jvm類(lèi)加載原理,也就是這個(gè)類(lèi)還是這個(gè)類(lèi),還是同一個(gè)ClassLoader
  • 還真有:java.lang.instrument.Instrumentation
Instrumentation
  • 我們來(lái)查一下Java API中對(duì)這個(gè)類(lèi)的描述信息:
該類(lèi)提供了用于設(shè)計(jì)Java編程語(yǔ)言代碼所需的服務(wù)。 儀器是向方法添加字節(jié)碼,用于收集工具要使用的數(shù)據(jù)。 由于這些更改純粹是加法的,因此這些工具不會(huì)修改應(yīng)用程序的狀態(tài)或行為。 這種良性工具的示例包括監(jiān)視代理,剖析器,覆蓋分析器和事件記錄器。
  • 文檔描述中有兩個(gè)我們能用得到的方法:redefineClasses和retransformClasses。一個(gè)重新定義class,一個(gè)修改class。這兩個(gè)差不多功能,
// 使用提供的類(lèi)文件重新定義提供的一組類(lèi)。 void redefineClasses(ClassDefinition... definitions)throws ClassNotFoundException,UnmodifiableClassException //重新轉(zhuǎn)換提供的一組類(lèi)。 void retransformClasses(類(lèi)<?>... classes)throws UnmodifiableClassException
  • 兩個(gè)方法都是替換已經(jīng)存在的class文件,redefineClasses是自己提供字節(jié)碼文件替換已經(jīng)存在的class,retransformClasses是在已經(jīng)存在的字節(jié)碼文件上修改后在替換。
  • 既然JDK提供了這種API,那么我們可以在編譯得到class文件后,在通過(guò)redefineClass替換,就能加日志,修改class文件,從而達(dá)到上文中不重啟修改的目的。
直接操作字節(jié)碼
  • 我們通過(guò)JDK的api來(lái)修改了本要加載的一個(gè)Class字節(jié)碼文件,字節(jié)碼文件也是程序語(yǔ)言,只不過(guò)人類(lèi)不好理解,可讀性遠(yuǎn)沒(méi)有java代碼高。
  • 一般人都不會(huì)去直接修改字節(jié)碼文件,但是,有一部分杰出的程序員,創(chuàng)造出來(lái)可以直接編輯字節(jié)碼的框架,提供接口可以讓我們方便的去操作字節(jié)碼文件,進(jìn)行注入修改類(lèi)的方法,動(dòng)態(tài)創(chuàng)造一個(gè)新的類(lèi)等等操作。其中最著名的就是ASM,現(xiàn)在我們接觸的cglib,Spring等框架中對(duì)字節(jié)碼的操作就是基于ASM上的,
實(shí)現(xiàn)方式
  • 截止目前都是針對(duì)開(kāi)始遇到的問(wèn)題的理論層面的可行性研究,那么我們?cè)趺磳?shí)現(xiàn),而且修改線上的字節(jié)碼文件簡(jiǎn)直就是在作死,而且實(shí)施起來(lái)會(huì)有意想不到的困難:

    • 尋找工程中的這個(gè)字節(jié)碼
    • 修改這個(gè)字節(jié)碼,然后reTransform這個(gè)字節(jié)碼
    • 我們無(wú)法預(yù)知某個(gè)程序出錯(cuò)需要去修改字節(jié)碼,也不可能每個(gè)工程都開(kāi)發(fā)一段專(zhuān)門(mén)的程序去修改字節(jié)碼,并且重新加載改代碼,這樣成本太高
    • 即使我們能解決上面的問(wèn)題,我們也不一定會(huì)用ASM,需要更通用的辦法
    • JVM不在本地,在遠(yuǎn)程,我們也是需要解決的問(wèn)題
    • 修改線上JVM中字節(jié)碼文件簡(jiǎn)直在作死,這存在巨大的安全性問(wèn)題
  • 幸運(yùn)的是,已經(jīng)有人吧這么都搞定了,因?yàn)橛幸粋€(gè)開(kāi)源工具BTrace的存在,描述非常精煉:

A safe, dynamic tracing tool for the Java platform
  • BTrace 是基于Java預(yù)約的一個(gè)安全,可提供動(dòng)態(tài)追蹤服務(wù)的工具。BTrace基于ASM,Java Attach API, Instruments開(kāi)發(fā),為用戶(hù)提供很多注解,依靠這些注解,可以編寫(xiě)B(tài)Trace腳本(簡(jiǎn)單的Java腳本)達(dá)到我們的目的,而不必對(duì)ASM有深刻理解。

  • 我們看下Attach 的api

  • 看BTrace官網(wǎng)的一個(gè)簡(jiǎn)單例子:攔截所有java.io包中所有類(lèi)以read開(kāi)頭的方法,打印類(lèi)名,方法名,參數(shù)名。當(dāng)程序IO負(fù)載比較高的時(shí)候,可以從輸出的信息看到是哪些類(lèi)引起的,如此的方便:

package com.sun.btrace.samples;import com.sun.btrace.annotations.*; import com.sun.btrace.AnyType; import static com.sun.btrace.BTraceUtils.*;/*** This sample demonstrates regular expression* probe matching and getting input arguments* as an array - so that any overload variant* can be traced in "one place". This example* traces any "readXX" method on any class in* java.io package. Probed class, method and arg* array is printed in the action.*/ @BTrace public class ArgArray {@OnMethod(clazz="/java\\.io\\..*/",method="/read.*/")public static void anyRead(@ProbeClassName String pcn, @ProbeMethodName String pmn, AnyType[] args) {println(pcn);println(pmn);printArray(args);} }
先理解在應(yīng)用
  • 既然BTrace能解決以上的問(wèn)題,那么他是怎么做到的,他的架構(gòu)是怎么樣的,我們可以從管網(wǎng)的信息中找到答案:

  • BTrace主要有一下幾個(gè)模塊

    • BTrace腳本:利用BTrace定義的注解,我們可以很方便的更具需要進(jìn)行腳本開(kāi)發(fā)
    • Compiler(編譯):將BTrace腳本編譯成BTrace class文件
    • Client:將Class文件發(fā)送到Agent
    • Agent:基于Java的Attach Api,Agent可以動(dòng)態(tài)附著一個(gè)運(yùn)行的JVM上,然后開(kāi)啟一個(gè)BTrace Server,接受Client發(fā)過(guò)來(lái)的BTrace腳本。解析腳本然后根據(jù)腳本中的規(guī)則找到要修改的類(lèi);然后調(diào)用Java Instruments的reTransform接口完成對(duì)象行為的修改并使之生效。

安全性
  • 如上流程最終借助Java instruments 和Java Attach api實(shí)現(xiàn)class替換,出于安全考慮,instruments在使用上會(huì)有諸多的限制:
    • 不允許創(chuàng)建對(duì)象
    • 不允許創(chuàng)建數(shù)組
    • 不允許拋異常
    • 不允許catch異常
    • 不允許隨意調(diào)用其他對(duì)象或者類(lèi)的方法,只允許調(diào)用com.sun.btrace.BTraceUtils中提供的靜態(tài)方法(一些數(shù)據(jù)處理和信息輸出工具)
    • 不允許改變類(lèi)的屬性
    • 不允許有成員變量和方法,只允許存在static public void方法
    • 不允許有內(nèi)部類(lèi)、嵌套類(lèi)
    • 不允許有同步方法和同步塊
    • 不允許有循環(huán)
    • 不允許隨意繼承其他類(lèi)(當(dāng)然,java.lang.Object除外)
    • 不允許實(shí)現(xiàn)接口
    • 不允許使用assert
    • 不允許使用Class對(duì)象
  • 如此多的限制,其實(shí)可以理解。BTrace要做的是,雖然修改了字節(jié)碼,但是除了輸出需要的信息外,對(duì)整個(gè)程序的正常運(yùn)行并沒(méi)有影響。
應(yīng)用
  • 應(yīng)用詳見(jiàn)本人git項(xiàng)目
Arthas
  • BTrace腳本使用上也有一定學(xué)習(xí)成本,如果吧常用功能封裝好,提供簡(jiǎn)單命令,那就編程了Archas
最后
  • 總結(jié)我們之前的知識(shí)點(diǎn),Java的Instruments給運(yùn)行時(shí)的動(dòng)態(tài)追蹤留下了一個(gè)初始APi,Attach API則給運(yùn)行時(shí)動(dòng)態(tài)追蹤提供了“出入口”,ASM則大大方便了“人類(lèi)”操作Java字節(jié)碼的操作。
  • 所以基于以上JDK api,前輩們創(chuàng)造出來(lái)諸如JProfiler, JVisualvm,BTrace,Archas這樣的工具。
  • 以上大大提高了軟件開(kāi)發(fā)人民定位問(wèn)題的效率。

總結(jié)

以上是生活随笔為你收集整理的Java动态追踪技术--BTrace的全部?jī)?nèi)容,希望文章能夠幫你解決所遇到的問(wèn)題。

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