1. 程式人生 > >分散式呼叫鏈(二)---Control/Service層插樁埋點實際處理

分散式呼叫鏈(二)---Control/Service層插樁埋點實際處理

為什麼要在Control/Service 層進行埋點

因為請求過來以後,先進入Control,在進入Service。

在Control:可以統計某個URL,具體的執行次數、時間、一段時間的流量統計。

在Service :可以統計某個方法的用時。

當有了資料以後,可以使用kibana來做視覺化工具。kibana是屬於elasticsearch的一款工具。 

採集端執行流程

應用系統嵌入監聽器,採集到資料以後,由http協議傳送給叢集,

在傳送給elasticsearch.(也可以直接進入DB,但是不靈活),

elasticsearch好處: 當遇到異常資訊以後,可以用其反向搜尋引擎,

採集流程

  • 判定誰是採集目標
  • 構建插樁後的Class位元組
  • 採集方法執行時資訊,如開始時間、結束時間、方法名
  • 上傳執行時資訊

 怎麼知道某個類是需要採集的類? 

 怎麼知道是Control還是service

根據UML圖,可以將共同點抽象出來,如所有類都要判定是否是目標,都要載入到ClassLoader。所有抽象出兩個介面:

 

但不是所有采集器不一定要記錄開始資訊、結束資訊、異常資訊、統計上傳資訊,只是一般情況會。所以可以抽象出一個抽象類。AbstractCollects。在上傳資訊的時候,需要用到執行緒,自定義一個threadService.

總之做設計時候,必須用到的用介面,通用的用抽象類。介面是用來簡化程式設計,遮蔽底層實現。

 採集器需要一個入口,定義AgentMain

 具體定義採集器,由於需要採集Controler、service、jdbc層的資訊,定義3個採集器。如果用到Dubbo,可以繼續定義。

將採集器註冊到AgentMain中,由於採集器不多,後期擴充套件慾望不是很強,可以採取硬編碼方式,也可以寫個xml檔案進行配置,方便插拔。

Service層的採集

//判定是否是service層程式碼
public boolean isTarget(String className, ClassLoader loader, CtClass ctclass) {
        try {
            //獲取class類的註解,然後遍歷。如果註解是@service,則命中。
            for (Object obj : ctclass.getAnnotations()) {
                if (obj.toString().startsWith("@org.springframework.stereotype.Service")) {
                    return true;
                }
            }
        } catch (ClassNotFoundException e) {
            System.err.println(String.format("bit apm run error targetClassName=%s errorMessage=%s",className,e.getClass().getSimpleName()+":"+e.getMessage()));
        }
        return false;
    }
public byte[] transform(ClassLoader loader, String className, byte[] classfileBuffer, CtClass ctclass) throws
            Exception {
        AgentLoader byteLoade = new AgentLoader(className, loader, ctclass);

        CtMethod[] methods = ctclass.getDeclaredMethods();
        for (CtMethod m : methods) {
            //排除寫法,比火箭寫法好,可讀性更高。火箭寫法作者邏輯清楚,但不利於可讀性。
            // 遮蔽非公共方法
            if (!Modifier.isPublic(m.getModifiers())) {
                continue;
            }
            // 遮蔽靜態方法
            if (Modifier.isStatic(m.getModifiers())) {
                continue;
            }
            // 遮蔽本地方法
            if (Modifier.isNative(m.getModifiers())) {
                continue;
            }

            AgentLoader.MethodSrcBuild build = new AgentLoader.MethodSrcBuild();
            build.setBeginSrc(String.format(beginSrc,className,m.getName()));
            build.setEndSrc(endSrc);
            build.setErrorSrc(errorSrc);
            byteLoade.updateMethod(m, build);
        }
        return byteLoade.toBytecote();
    }

由於封裝的時候,涉及到執行前,執行後,與出異常3種情況,所以定義三個方法。