arthas最底層的java agent原理

\color{red}{歡迎關(guān)注我的微信公眾號:進(jìn)階者euj}
上一篇文章不停機不更新代碼線上調(diào)試BUG的工具
教會了大家如果使用arthas是定位系統(tǒng)線上問題
這篇文章教大家關(guān)于arthas的原理
arthas其實也是利用是了java agent。
這個是jvm提供出來的接口能扒,在類加載之前會觸發(fā)一個方法嘀略,讓大家自定義自己想要切入的業(yè)務(wù);

如何寫一個自己的java agent

1天试、新建一個springboot的工程,用來模擬自己的app應(yīng)用,應(yīng)用每5秒會調(diào)用一個方法

package com.eujian.arthaslearn.controller;

public class MyService {

    public String send(){
        System.out.println("send被調(diào)用了");
        try {
            Thread.sleep(1000);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        return "send";
    }
}

github地址 https://github.com/hd-eujian/arthas-learn.git
碼云地址 https://gitee.com/guoeryyj/arthas-learn.git

2素挽、新建 agent工程照卦,打包成jar包式矫,去做class文件的aop
pom文件引入依賴

 <build>
        <plugins>
            <plugin>
                <groupId>org.apache.maven.plugins</groupId>
                <artifactId>maven-assembly-plugin</artifactId>
                <configuration>
                    <descriptorRefs>
                        <descriptorRef>jar-with-dependencies</descriptorRef>
                    </descriptorRefs>
                    <archive>
                        <manifestEntries>
                            <Premain-Class>com.eujian.agent.PreMainTraceAgent</Premain-Class>
                            <Agent-Class>com.eujian.agent.PreMainTraceAgent</Agent-Class>
                            <Can-Redefine-Classes>true</Can-Redefine-Classes>
                            <Can-Retransform-Classes>true</Can-Retransform-Classes>
                        </manifestEntries>
                    </archive>
                </configuration>

            </plugin>
        </plugins>
    </build>

新建文件PreMainTraceAgent

package com.eujian.agent;

import java.lang.instrument.Instrumentation;

public class PreMainTraceAgent {
    public static void agentmain (String agentArgs, Instrumentation inst) throws Exception {
        System.out.println("agent begin agentArgs="+agentArgs);
        MyClassFileTransformer myClassFileTransformer;

        //如果入?yún)⑹?就清除aop
        if("1".equals(agentArgs)){
            myClassFileTransformer = new MyClassFileTransformer(true);
            inst.addTransformer(myClassFileTransformer,true);
        }else {
            myClassFileTransformer = new MyClassFileTransformer();
            inst.addTransformer(myClassFileTransformer,true);
        }
        System.out.println("agent end");

        Class[] allLoadedClasses = inst.getAllLoadedClasses();
        for (Class clazz : allLoadedClasses){
            if(clazz.getName().contains("com.eujian.arthaslearn.controller.MyService")){
                inst.retransformClasses(clazz);
                System.out.println("重新加載"+clazz);
            }
        }
    }
}

新建文件MyClassFileTransformer實現(xiàn)接口ClassFileTransformer

package com.eujian.agent;

import javassist.ClassPool;
import javassist.CtClass;
import javassist.CtMethod;

import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.lang.instrument.ClassFileTransformer;
import java.lang.instrument.IllegalClassFormatException;
import java.security.ProtectionDomain;

public class MyClassFileTransformer implements ClassFileTransformer {

    private boolean isReLoad = false;

    public MyClassFileTransformer() {
    }

    public MyClassFileTransformer(boolean isReLoad) {
        this.isReLoad = isReLoad;
    }

    @Override
    public byte[] transform(ClassLoader loader, String className, Class<?> classBeingRedefined, ProtectionDomain protectionDomain, byte[] classfileBuffer) throws IllegalClassFormatException {

        if (!className.equals("com/eujian/arthaslearn/controller/MyService")) {
            return null;
        }
        try {

            System.out.println("進(jìn)入 isReLoad:"+isReLoad);
            System.out.println("進(jìn)入 className:"+className);
            System.out.println("進(jìn)入 loader:"+loader);
            System.out.println("進(jìn)入 classBeingRedefined:"+classBeingRedefined);
            CtClass cl = null;
            ClassPool classPool = ClassPool.getDefault();
            cl = classPool.getCtClass("com.eujian.arthaslearn.controller.MyService");

            System.out.println("cl.isFrozen()+"+cl.isFrozen());
            if(isReLoad){
                //重新加載本地class文件
                cl.defrost();
                return readStream(ClassLoader.getSystemResourceAsStream(className.replace('.', '/') + ".class"), true);
            }
            CtMethod method = cl.getDeclaredMethod("send");

            //插入你想要的代碼
            method.insertBefore("System.out.println(\"send-begin\");");
            method.insertAfter("System.out.println(\"send-end\");");

            byte[] transformed = cl.toBytecode();

            cl.detach();
            return transformed;

        }catch (Exception e){
            e.printStackTrace();
        }
        return null;
    }
    private static byte[] readStream(InputStream inputStream, boolean close) throws IOException {
        if(inputStream == null) {
            throw new IOException("Class not found");
        } else {
            try {
                ByteArrayOutputStream outputStream = new ByteArrayOutputStream();
                byte[] data = new byte[4096];

                int bytesRead;
                while((bytesRead = inputStream.read(data, 0, data.length)) != -1) {
                    outputStream.write(data, 0, bytesRead);
                }

                outputStream.flush();
                byte[] var5 = outputStream.toByteArray();
                return var5;
            } finally {
                if(close) {
                    inputStream.close();
                }

            }
        }
    }
}

執(zhí)行命令打包mvn clean assembly:assembly
github 地址:https://github.com/hd-eujian/agent.git
碼云地址: https://gitee.com/guoeryyj/agent.git

新建一個main函數(shù)的工程,工程主要作用是把打包的jar包注入到運行的app中
代碼如下

import com.sun.tools.attach.VirtualMachine;
import com.sun.tools.attach.VirtualMachineDescriptor;

import java.util.List;

public class Main {

    public static void main(String[] args) throws Exception{
        System.out.println("running JVM start ");
        String jarUrl = "/xxx/agent/target/agent-1.0-SNAPSHOT-jar-with-dependencies.jar";
        List<VirtualMachineDescriptor> list = VirtualMachine.list();
        for (VirtualMachineDescriptor vmd : list) {
            if (vmd.displayName().contains("ArthasLearnApplication")) {
                VirtualMachine virtualMachine = VirtualMachine.attach(vmd.id());

                //這個是加載jar包增強class文件
                virtualMachine.loadAgent(jarUrl);

                //這個是恢復(fù)class文件
//                virtualMachine.loadAgent(jarUrl,"1");
                virtualMachine.detach();
            }
        }
    }
}

實操環(huán)節(jié)

1役耕、先啟動arthas-learn工程


arthas-learn工程

2采转、agent工程打包
mvn clean assembly:assembly

3、執(zhí)行main函數(shù)


注入jar包后的效果

\color{red}{歡迎關(guān)注我的微信公眾號:進(jìn)階者euj}

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市故慈,隨后出現(xiàn)的幾起案子板熊,更是在濱河造成了極大的恐慌,老刑警劉巖察绷,帶你破解...
    沈念sama閱讀 206,482評論 6 481
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件干签,死亡現(xiàn)場離奇詭異,居然都是意外死亡拆撼,警方通過查閱死者的電腦和手機容劳,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 88,377評論 2 382
  • 文/潘曉璐 我一進(jìn)店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來闸度,“玉大人竭贩,你說我怎么就攤上這事〗畹海” “怎么了娶视?”我有些...
    開封第一講書人閱讀 152,762評論 0 342
  • 文/不壞的土叔 我叫張陵,是天一觀的道長睁宰。 經(jīng)常有香客問我肪获,道長,這世上最難降的妖魔是什么柒傻? 我笑而不...
    開封第一講書人閱讀 55,273評論 1 279
  • 正文 為了忘掉前任孝赫,我火速辦了婚禮,結(jié)果婚禮上红符,老公的妹妹穿的比我還像新娘青柄。我一直安慰自己,他們只是感情好预侯,可當(dāng)我...
    茶點故事閱讀 64,289評論 5 373
  • 文/花漫 我一把揭開白布致开。 她就那樣靜靜地躺著,像睡著了一般萎馅。 火紅的嫁衣襯著肌膚如雪双戳。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 49,046評論 1 285
  • 那天糜芳,我揣著相機與錄音飒货,去河邊找鬼。 笑死峭竣,一個胖子當(dāng)著我的面吹牛塘辅,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播皆撩,決...
    沈念sama閱讀 38,351評論 3 400
  • 文/蒼蘭香墨 我猛地睜開眼扣墩,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了?” 一聲冷哼從身側(cè)響起沮榜,我...
    開封第一講書人閱讀 36,988評論 0 259
  • 序言:老撾萬榮一對情侶失蹤盘榨,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后蟆融,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 43,476評論 1 300
  • 正文 獨居荒郊野嶺守林人離奇死亡守呜,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 35,948評論 2 324
  • 正文 我和宋清朗相戀三年型酥,在試婚紗的時候發(fā)現(xiàn)自己被綠了。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片查乒。...
    茶點故事閱讀 38,064評論 1 333
  • 序言:一個原本活蹦亂跳的男人離奇死亡弥喉,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出玛迄,到底是詐尸還是另有隱情由境,我是刑警寧澤,帶...
    沈念sama閱讀 33,712評論 4 323
  • 正文 年R本政府宣布蓖议,位于F島的核電站虏杰,受9級特大地震影響,放射性物質(zhì)發(fā)生泄漏勒虾。R本人自食惡果不足惜纺阔,卻給世界環(huán)境...
    茶點故事閱讀 39,261評論 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望修然。 院中可真熱鬧笛钝,春花似錦、人聲如沸愕宋。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,264評論 0 19
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽中贝。三九已至囤捻,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間雄妥,已是汗流浹背最蕾。 一陣腳步聲響...
    開封第一講書人閱讀 31,486評論 1 262
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留老厌,地道東北人瘟则。 一個月前我還...
    沈念sama閱讀 45,511評論 2 354
  • 正文 我出身青樓,卻偏偏與公主長得像枝秤,于是被迫代替她去往敵國和親醋拧。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點故事閱讀 42,802評論 2 345