分散式呼叫鏈(二)---Control/Service層插樁埋點實際處理
阿新 • • 發佈:2019-01-28
為什麼要在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種情況,所以定義三個方法。