<kbd id="afajh"><form id="afajh"></form></kbd>
<strong id="afajh"><dl id="afajh"></dl></strong>
    <del id="afajh"><form id="afajh"></form></del>
        1. <th id="afajh"><progress id="afajh"></progress></th>
          <b id="afajh"><abbr id="afajh"></abbr></b>
          <th id="afajh"><progress id="afajh"></progress></th>

          pfinder實(shí)現(xiàn)原理揭秘

          共 18757字,需瀏覽 38分鐘

           ·

          2024-06-28 11:10

          引言

          在現(xiàn)代軟件開發(fā)過(guò)程中,性能優(yōu)化和故障排查是保證應(yīng)用穩(wěn)定運(yùn)行的關(guān)鍵任務(wù)之一。Java作為一種廣泛使用的編程語(yǔ)言,其生態(tài)中涌現(xiàn)出了許多優(yōu)秀的監(jiān)控和診斷工具,諸如:SkyWalking、Zipkin等,它們幫助開發(fā)者和運(yùn)維人員深入了解應(yīng)用的運(yùn)行狀態(tài),快速定位和解決問(wèn)題。在京東內(nèi)部,則使用的是自研的pfinder。

          本文旨在深入探討pfinder的核心原理和架構(gòu)設(shè)計(jì),揭示它是如何實(shí)現(xiàn)應(yīng)用全鏈路監(jiān)控的。我們將從pfinder的基本概念和功能開始講起,逐步深入到其具體實(shí)現(xiàn)機(jī)制。
          pfinder概述

          pfinder簡(jiǎn)介

          PFinder (problem finder) 是UMP團(tuán)隊(duì)打造的新一代APM(應(yīng)用性能追蹤)系統(tǒng),集調(diào)用鏈追蹤、應(yīng)用拓?fù)?、多維監(jiān)控于一身,無(wú)需修改代碼,只需要在啟動(dòng)文件增加 2 行腳本,便可實(shí)現(xiàn)接入。接入后便會(huì)對(duì)應(yīng)用提供可觀測(cè)能力,目前支持京東主流的中間件,包括:jimdb,jmq,jsf,以及一些常用的開源組件:tomcat、http client,mysql,es等。

          pfinder功能

          PFinder 除了具備 ump 現(xiàn)有功能的基礎(chǔ)上,增加了以下重磅功能:
          ?多維監(jiān)控: 支持按多個(gè)維度統(tǒng)計(jì)監(jiān)控指標(biāo),按機(jī)房、按分組、按JSF別名、按調(diào)用方,各種維度隨心組合查看
          ?自動(dòng)埋點(diǎn): 自動(dòng)對(duì) SpringMVC,JSF,MySQL,JMQ 等常用中間件進(jìn)行性能埋點(diǎn),無(wú)需改動(dòng)代碼,接入即可觀測(cè)
          ?應(yīng)用拓?fù)? 自動(dòng)梳理服務(wù)的上下游和中間件的依賴拓?fù)?/span>
          ?調(diào)用鏈追蹤: 基于請(qǐng)求的跨服務(wù)調(diào)用追蹤,助你快速分析性能瓶頸
          ?自動(dòng)故障分析: 通過(guò)AI算法自動(dòng)分析調(diào)用拓?fù)渖纤蟹?wù)的監(jiān)控?cái)?shù)據(jù),自動(dòng)判斷故障根因
          ?流量錄制回放: 通過(guò)錄制線上流量,回放至待特定環(huán)境(測(cè)試、預(yù)發(fā)),對(duì)比回放與錄制時(shí)產(chǎn)生的差異,幫助用戶補(bǔ)全業(yè)務(wù)場(chǎng)景、完善測(cè)試用例
          ?跨單元逃逸流量監(jiān)控: 支持 JSF 跨單元流量、逃逸流量監(jiān)控,單元化應(yīng)用運(yùn)行狀態(tài)一目了然

          APM類組件對(duì)比

          更重要的一點(diǎn)是:pfinder對(duì)京東內(nèi)部自研組件提供了支持,比如:jsf、jmq、jimdb
          pfinder背后的秘密
          既然pfinder是基于字節(jié)碼增強(qiáng)實(shí)現(xiàn)的,那么講到pfinder,字節(jié)碼增強(qiáng)技術(shù)自然也是無(wú)法避開的話題。這里我將字節(jié)碼增強(qiáng)技術(shù)分兩點(diǎn)來(lái)說(shuō),也是我認(rèn)為實(shí)現(xiàn)字節(jié)碼增強(qiáng)需要解決的兩個(gè)關(guān)鍵點(diǎn):
          1.字節(jié)碼是為了機(jī)器設(shè)計(jì)的,而非人類,字節(jié)碼可讀性極差、修改門檻極高,那么我們?nèi)绾涡薷淖止?jié)碼呢?
          2.修改后的字節(jié)碼如何注入運(yùn)行時(shí)JVM中呢?
          欲攻善其事,必先利其器,所以下面我們圍繞著這兩個(gè)問(wèn)題進(jìn)行展開,當(dāng)然,對(duì)這方面知識(shí)已經(jīng)有所掌握的同學(xué)可忽略。

          字節(jié)碼修改

          字節(jié)碼修改成熟的框架已經(jīng)很多了,諸如:ASM、javassist、bytebuddy、bytekit,下面我們用這幾個(gè)字節(jié)碼修改框架實(shí)現(xiàn)一個(gè)相同的功能,來(lái)對(duì)比下這幾個(gè)框架使用上的區(qū)別?,F(xiàn)在我們通過(guò)字節(jié)碼修改來(lái)實(shí)現(xiàn)下面的功能:
          ?

          ASM實(shí)現(xiàn)

             @Override        public void visitCode() {            super.visitCode();            mv.visitFieldInsn(GETSTATIC, "java/lang/System", "out", "Ljava/io/PrintStream;");            mv.visitLdcInsn("start");            mv.visitMethodInsn(INVOKEVIRTUAL, "java/io/PrintStream", "println", "(Ljava/lang/String;)V", false);        }        @Override        public void visitInsn(int opcode) {            if ((opcode >= Opcodes.IRETURN && opcode <= Opcodes.RETURN) || opcode == Opcodes.ATHROW) {                //方法在返回之前,打印"end"                mv.visitFieldInsn(GETSTATIC, "java/lang/System", "out", "Ljava/io/PrintStream;");                mv.visitLdcInsn("end");                mv.visitMethodInsn(INVOKEVIRTUAL, "java/io/PrintStream", "println", "(Ljava/lang/String;)V", false);            }            mv.visitInsn(opcode);        }

          javassist實(shí)現(xiàn)

                  ClassPool cp = ClassPool.getDefault();        CtClass cc = cp.get("com.ggc.javassist.HelloWord");        CtMethod m = cc.getDeclaredMethod("printHelloWord");        m.insertBefore("{ System.out.println(\"start\"); }");        m.insertAfter("{ System.out.println(\"end\"); }");        Class c = cc.toClass();        cc.writeFile("/Users/gonghanglin/workspace/workspace_me/bytecode_enhance/bytecode_enhance_javassist/target/classes/com/ggc/javassist");        HelloWord h = (HelloWord)c.newInstance();        h.printHelloWord();

          bytebuddy實(shí)現(xiàn)

             // 使用ByteBuddy動(dòng)態(tài)生成一個(gè)新的HelloWord類        Class<?> dynamicType = new ByteBuddy()                .subclass(HelloWord.class) // 指定要修改的類                .method(ElementMatchers.named("printHelloWord")) // 指定要攔截的方法名                .intercept(MethodDelegation.to(LoggingInterceptor.class)) // 指定攔截器                .make()                .load(HelloWord.class.getClassLoader()) // 加載生成的類                .getLoaded();
          // 創(chuàng)建動(dòng)態(tài)生成類的實(shí)例,并調(diào)用方法 HelloWord dynamicService = (HelloWord) dynamicType.newInstance(); dynamicService.printHelloWord();
          public class LoggingInterceptor {    @RuntimeType    public static Object intercept(@AllArguments Object[] allArguments, @Origin Method method, @SuperCall Callable<?> callable) throws Exception {        // 打印start        System.out.println("start");        try {            // 調(diào)用原方法            Object result = callable.call();            // 打印end            System.out.println("end");            return result;        } catch (Exception e) {            System.out.println("exception end");            throw e;        }    }}

          bytekit實(shí)現(xiàn)

           // Parse the defined Interceptor class and related annotations        DefaultInterceptorClassParser interceptorClassParser = new DefaultInterceptorClassParser();        List<InterceptorProcessor> processors = interceptorClassParser.parse(HelloWorldInterceptor.class);        // load bytecode        ClassNode classNode = AsmUtils.loadClass(HelloWord.class);        // Enhanced process of loaded bytecodes        for (MethodNode methodNode : classNode.methods) {            MethodProcessor methodProcessor = new MethodProcessor(classNode, methodNode);            for (InterceptorProcessor interceptor : processors) {                interceptor.process(methodProcessor);            }        }

          ?

          public class HelloWorldInterceptor {    @AtEnter(inline = true)    public static void atEnter() {        System.out.println("start");    }
          @AtExit(inline = true) public static void atEit() { System.out.println("end"); }}
          特性 ASM Javassist ByteBuddy ByteKit
          性能 ASM的性能最高,因為它直接操作字節(jié)碼,沒有中間環(huán)節(jié) 劣于ASM 介于javassist和ASM之間 介于javassist和ASM之間
          易用性 需精通字節(jié)碼,學(xué)習(xí)成本高,不支持debug Java語(yǔ)法進(jìn)行開發(fā),但是采用的硬編碼形式開發(fā),不支持debug 比Javassist更高級(jí),更符文Java開發(fā)習(xí)慣,可以對(duì)增強(qiáng)代碼進(jìn)行斷點(diǎn)調(diào)試 比Javassist更高級(jí),更符文Java開發(fā)習(xí)慣,可以對(duì)增強(qiáng)代碼進(jìn)行斷點(diǎn)調(diào)試
          功能 直接操作字節(jié)碼,功能最為強(qiáng)大。 功能相對(duì)完備 功能相對(duì)完備 功能相對(duì)完備,對(duì)比ByteBuddy,ByteKit能防止重復(fù)增強(qiáng)


          字節(jié)碼注入

          相信大家經(jīng)常使用idea去debug我們寫的代碼,我們是否想過(guò)debug是如何實(shí)現(xiàn)的呢?暫時(shí)先賣個(gè)關(guān)子。

          JVMTIAgent

          JVM在設(shè)計(jì)之初就考慮到了對(duì)JVM運(yùn)行時(shí)內(nèi)存、線程等指標(biāo)的監(jiān)控和分析和代碼debug功能的實(shí)現(xiàn),基于這兩點(diǎn),早在JDK5之前,JVM規(guī)范就定義了JVMPI(JVM分析接口)和JVMDI(JVM調(diào)試接口),JDK5之后,這兩個(gè)規(guī)范就合并成為了JVMTI(JVM工具接口)。JVMTI其實(shí)是一種JVM規(guī)范,每個(gè)JVM廠商都有不同的實(shí)現(xiàn),另外,JVMTI接口需使用C語(yǔ)言開發(fā),以動(dòng)態(tài)鏈接的形式加載并運(yùn)行。


          JVMTI接口
          接口 功能
          Agent_OnLoad(JavaVM *vm, char *options, void *reserved); agent在啟動(dòng)時(shí)加載的情況下,也就是在vm參數(shù)里通過(guò)-agentlib來(lái)指定,那在啟動(dòng)過(guò)程中就會(huì)去執(zhí)行這個(gè)agent里的Agent_OnLoad函數(shù)。
          Agent_OnAttach(JavaVM* vm, char* options, void* reserved); agent是attach到目標(biāo)進(jìn)程上,然后給對(duì)應(yīng)的目標(biāo)進(jìn)程發(fā)送load命令來(lái)加載agent,在加載過(guò)程中就會(huì)調(diào)用Agent_OnAttach函數(shù)。
          Agent_OnUnload(JavaVM *vm); 在agent卸載的時(shí)候調(diào)用


          其實(shí)idea的debug功能便是借助JVMTI實(shí)現(xiàn)的,具體說(shuō)是利用了jre內(nèi)置的jdwp agent來(lái)實(shí)現(xiàn)的。我們?cè)趇dea中debug程序時(shí),控制臺(tái)命令如下:
          ?
          這里agentlib參數(shù)就是用來(lái)跟要加載的agent的名字,比如這里的jdwp(不過(guò)這不是動(dòng)態(tài)庫(kù)的名字,而JVM是會(huì)做一些名稱上的擴(kuò)展,比如在MACOS下會(huì)去找libjdwp.dylib的動(dòng)態(tài)庫(kù)進(jìn)行加載,也就是在名字的基礎(chǔ)上加前綴lib,再加后綴.dylib)。
          1.instrument
          上面說(shuō)到JVMTIAgent基于C語(yǔ)言開發(fā),以動(dòng)態(tài)鏈接的形式加載并運(yùn)行,這對(duì)java開發(fā)者不太友好。JDK5之后,JDK開始提供java.lang.instrument.Instrumentation接口,讓開發(fā)者可以使用Java語(yǔ)言編寫Agent。其實(shí),instrument也是基于JVMTI實(shí)現(xiàn)的,在MACOS下instrument動(dòng)態(tài)庫(kù)名為libinstrument.dylib。
          instrument主要方法
          方法 功能
          void addTransformer(ClassFileTransformer transformer) 添加一個(gè)字節(jié)碼轉(zhuǎn)換器,用來(lái)修改加載類的字節(jié)碼
          Class[] getAllLoadedClasses() 返回當(dāng)前JVM中加載的所有的類的數(shù)組
          Class[] getInitiatedClasses(ClassLoader loader) 返回指定的類加載器中的所有的類的數(shù)據(jù)
          void redefineClasses(ClassDefinition... definitions) 用給定的類的字節(jié)碼數(shù)組替換指定的類的字節(jié)碼文件,也就是重新定義指定的類
          void retransformClasses(Class<?>... classes) 指定一系列的Class對(duì)象,被指定的類都會(huì)重新變回去(去掉附加的字節(jié)碼)
          1.instrument和ByteBuddy實(shí)現(xiàn)javaagent打印方法耗時(shí)
          1.agent包MANIFEST.MF配置(maven插件)
          <archive>   <manifestEntries>       // 指定premain()的所在方法       <Agent-CLass>com.ggc.agent.GhlAgent</Agent-CLass>       <Premain-Class>com.ggc.agent.GhlAgent</Premain-Class>       <Can-Redefine-Classes>true</Can-Redefine-Classes>       <Can-Retransform-Classes>true</Can-Retransform-Classes>   </manifestEntries></archive>               

          2.agen主類

          public class GhlAgent {    public static Logger log = LoggerFactory.getLogger(GhlAgent.class);
          public static void agentmain(String agentArgs, Instrumentation instrumentation) { log.info("agentmain方法"); boot(instrumentation); } public static void premain(String agentArgs, Instrumentation instrumentation) { log.info("premain方法"); boot(instrumentation); } private static void boot(Instrumentation instrumentation) { //創(chuàng)建一個(gè)代理增強(qiáng)對(duì)象 new AgentBuilder.Default().type(ElementMatchers.nameStartsWith("com.jd.aviation.performance.service.impl"))//攔截指定的類 .transform((builder, typeDescription, classLoader, javaModule) -> builder.method(ElementMatchers.isMethod().and(ElementMatchers.isPublic()) ).intercept(MethodDelegation.to(TimingInterceptor.class)) ).installOn(instrumentation); }}

                 3.攔截器

          public class TimingInterceptor {    public static Logger log = LoggerFactory.getLogger(TimingInterceptor.class);    @RuntimeType    public static Object intercept(@SuperCall Callable<?> callable) throws Exception {        long start = System.currentTimeMillis();        try {            // 原方法調(diào)用            return callable.call();        } finally {            long end = System.currentTimeMillis();            log.info("Method call took {} ms",(end - start));        }    }}

              4.效果

          ?
          1.pfinder實(shí)現(xiàn)原理

          4.1.pfinder應(yīng)用架構(gòu)

          ?
          1.pfinder agent啟動(dòng)時(shí)首先加載META-INF/pfinder/service.addon和META-INF/pfinder/plugin.addon配置文件中的服務(wù)和插件。2.根據(jù)加載的插件做字節(jié)碼增強(qiáng)。3.使用JMTP將服務(wù)和插件產(chǎn)生的數(shù)據(jù)(trace、指標(biāo)等)進(jìn)行上報(bào)。

          4.2.pfinder插件增強(qiáng)代碼解析

          1.service加載
          ?
          創(chuàng)建SimplePFinderServiceLoader實(shí)例,在profilerBootstrap.boot(serviceLoaders)方法中加載配置文件中的service。
          ?
          使用創(chuàng)建的SimplePFinderServiceLoader實(shí)例加載service,并返回一個(gè)service工廠的迭代器。
          ?
          真正的加載走的是AddonLoader中的load方法。service加載完成后,繼續(xù)看bootService方法:
          ?
          bootService中完成創(chuàng)建service實(shí)例、注冊(cè)service、初始化service,service的加載至此就完成了。
          1.plugin加載&字節(jié)碼增強(qiáng)
          在介紹插件加載前,我們先了解下插件的包含了哪些信息。
          ?
          增強(qiáng)攔截器:這個(gè)類里面放了具體的增強(qiáng)邏輯
          增強(qiáng)點(diǎn)類型:增強(qiáng)時(shí)根據(jù)不同類型走不同邏輯
          增強(qiáng)類/方法匹配器:用于匹配需要增強(qiáng)的類/方法
          InterceptPoint是個(gè)數(shù)組,增強(qiáng)點(diǎn)可以配置多個(gè)。
          plugin的加載和字節(jié)碼增強(qiáng)發(fā)生在初始化service過(guò)程中,具體地說(shuō)發(fā)生在com.jd.pfinder.profiler.service.impl.PluginRegistrar這個(gè)service初始化的過(guò)程中了。
           protected boolean doInitialize(ProfilerContext profilerContext) {     AgentEnvService agentEnvService = (AgentEnvService)profilerContext.getService(AgentEnvService.class);     Instrumentation instrumentation = agentEnvService.instrumentation();     if (instrumentation == null) {       LOGGER.info("Instrumentation missing, PFinder PluginRegistrar enhance ignored!");       return false;     }     this.pluginLoaders = profilerContext.getAllService(PluginLoader.class);     this.enhanceHandler = new EnhancePluginHandler(profilerContext);     ElementMatcher.Junction<TypeDescription> typeMatcherChain = null;     for (PluginLoader pluginLoader : this.pluginLoaders) {       pluginLoader.loadPlugins(profilerContext);
          for (ElementMatcher.Junction<TypeDescription> typeMatcher : (Iterable<ElementMatcher.Junction<TypeDescription>>)pluginLoader.typeMatchers()) { if (typeMatcherChain == null) { typeMatcherChain = typeMatcher; continue; } typeMatcherChain = typeMatcherChain.or((ElementMatcher)typeMatcher); } } if (typeMatcherChain == null) { LOGGER.warn("no any enhance-point. pfinder enhance will be ignore."); return false; } ConfigurationService configurationService = (ConfigurationService)profilerContext.getService(ConfigurationService.class); String enhanceExcludePolicy = (String)configurationService.get(ConfigKey.PLUGIN_ENHANCE_EXCLUDE);
          LoadedClassSummaryHandler loadedClassSummaryHandler = null; if (((Boolean)configurationService.get(ConfigKey.LOADED_CLASSES_SUMMARY_ENABLED, Boolean.valueOf(false))).booleanValue()) { loadedClassSummaryHandler = new LoadedClassSummaryHandler.DefaultImpl(configurationService, ((ScheduledService)profilerContext.getService(ScheduledService.class)).getDefault()); }
          (new AgentBuilder.Default())
          .with(AgentBuilder.RedefinitionStrategy.RETRANSFORMATION) .with(AgentBuilder.RedefinitionStrategy.REDEFINITION) .with(new AgentBuilder.RedefinitionStrategy.Listener() { public void onBatch(int index, List<Class<?>> batch, List<Class<?>> types) {} public Iterable<? extends List<Class<?>>> onError(int index, List<Class<?>> batch, Throwable throwable, List<Class<?>> types) { return Collections.emptyList(); } public void onComplete(int amount, List<Class<?>> types, Map<List<Class<?>>, Throwable> failures) { for (Map.Entry<List<Class<?>>, Throwable> entry : failures.entrySet()) { for (Class<?> aClass : entry.getKey()) { PluginRegistrar.LOGGER.warn("Redefine class: {} failure! ignored!", new Object[] { aClass.getName(), entry.getValue() }); }
          } } }).ignore((ElementMatcher)ElementMatchers.nameStartsWith("org.groovy.") .or((ElementMatcher)ElementMatchers.nameStartsWith("jdk.nashorn.")) .or((ElementMatcher)ElementMatchers.nameStartsWith("javax.script.")) .or((ElementMatcher)ElementMatchers.nameContains("javassist")) .or((ElementMatcher)ElementMatchers.nameContains(".asm.")) .or((ElementMatcher)ElementMatchers.nameContains("$EnhancerBySpringCGLIB$")) .or((ElementMatcher)ElementMatchers.nameStartsWith("sun.reflect")) .or((ElementMatcher)ElementMatchers.nameStartsWith("org.apache.jasper")) .or((ElementMatcher)pfinderIgnoreMather()) .or((ElementMatcher)Matchers.forPatternLine(enhanceExcludePolicy)) .or((ElementMatcher)ElementMatchers.isSynthetic()))
          .type((ElementMatcher)typeMatcherChain) .transform(this) .with(new Listener(loadedClassSummaryHandler)) .installOn(instrumentation); return true; }
          第8行,先從上下文中取出注冊(cè)的PluginLoader(插件加載器),第12行遍歷插件加載器加載插件,插件加載邏輯其實(shí)和service一樣,使用的都是AddonLoader中的load方法。插件加載完成之后被插件加載器持有,第14-19行則收集插件中增強(qiáng)類的匹配器,用于AgentBuilder的創(chuàng)建。AgentBuilder的創(chuàng)建標(biāo)志著字節(jié)碼增強(qiáng)的開始,具體的邏輯在transform的實(shí)例方法中。
          ??transform方法中遍歷插件,enhance方法中對(duì)各個(gè)插件做增強(qiáng)。
          ??enhance方法中遍歷各個(gè)插件的增強(qiáng)點(diǎn)數(shù)組走enhanceInterceptPoint方法做增強(qiáng)。
          ??enhanceInterceptPoint方法中根據(jù)增強(qiáng)點(diǎn)類型做增強(qiáng)。
          ?
          上圖是以Advice方式增強(qiáng)實(shí)例方法,傳遞了interceptorFieldAppender和methodCacheFieldAppender兩個(gè)參數(shù),并使用AdviceMethodEnhanceInvoker訪問(wèn)并修改待增強(qiáng)的類和方法。AdviceMethodEnhanceInvoker中有onMethodEnter、onMethodExit兩個(gè)方法,分別表示進(jìn)入方法后和退出方法前。
          ?
          ?
          AdviceMethodEnhanceInvoker中onMethodEnter、onMethodExit兩個(gè)方法還會(huì)調(diào)用插件中配置interceptor對(duì)應(yīng)的onMethodEnter、onMethodExit、onException方法,至此插件字節(jié)碼增強(qiáng)就結(jié)束了。
          1.我的思考

          5.1.多線程traceId丟失問(wèn)題

          pfinder目前已經(jīng)將traceId放到了MDC中,我們通過(guò)在日志配置文件中添加[%X{PFTID}]便能在日志中打印traceId。但是我們知道MDC使用的是ThreadLocal去保存的traceId,在跨線程時(shí)會(huì)出現(xiàn)線程丟失的情況。pfinder在這方面做了字節(jié)碼增強(qiáng),無(wú)論使用線程池還是@Async,都不會(huì)存在traceId丟失的問(wèn)題。
           public class TracingRunnable   implements PfinderWrappedRunnable {   private final Runnable origin;   private final TracingSnapshot<?> snapshot;   private final Component component;   private final String operationName;   private final String interceptorName;   private final InterceptorClassLoader interceptorClassLoader;
          public TracingRunnable(Runnable origin, TracingSnapshot<?> snapshot, Component component, String operationName, String interceptorName, InterceptorClassLoader interceptorClassLoader) { this.origin = origin; this.snapshot = snapshot; this.component = component; this.operationName = operationName; this.interceptorClassLoader = interceptorClassLoader; this.interceptorName = interceptorName; } public void run() { TracingContext tracingContext = ContextManager.tracingContext(); if (tracingContext.isTracing() && tracingContext.traceId().equals(this.snapshot.getTraceId())) { this.origin.run(); return; } LowLevelAroundTracingContext context = SpringAsyncTracingContext.create(this.operationName, this.interceptorName, this.snapshot, this.interceptorClassLoader, this.component); context.onMethodEnter(); try { this.origin.run(); } catch (RuntimeException ex) { context.onException(ex); throw ex; } finally { context.onMethodExit(); } } public Runnable getOrigin() { return this.origin; } public String toString() { return "TracingRunnable{origin=" + this.origin + ", snapshot=" + this.snapshot + ", component=" + this.component + ", operationName='" + this.operationName + '\'' + '}'; } }

           拿線程池執(zhí)行Runnable任務(wù)來(lái)說(shuō),pfinder通過(guò)TracingRunnable包裝我們的Runnable的實(shí)現(xiàn),利用構(gòu)造函數(shù)將主線程的traceId通過(guò)snapshot參數(shù)傳給TracingRunnable,在run方法中將參數(shù)snapshot放到上下文中,最后從上下文中取出放到子線程的MDC中,從而實(shí)現(xiàn)traceId跨線程傳遞。

          5.2.熱部署

          既然javaagent能做字節(jié)碼增強(qiáng),也能實(shí)現(xiàn)熱部署,此外, pfinder客戶端和服務(wù)端通過(guò)jmtp有命令的交互,可以通過(guò)服務(wù)端向agent發(fā)送命令來(lái)實(shí)現(xiàn)類搜索、反編譯、熱更新等功能,筆者基于這一想法粗略實(shí)現(xiàn)了一個(gè)在線熱部署的功能,具體如下:
          類搜索:

          ??反編譯:

          ??熱更新:
          ?
          上述只是筆者做的一個(gè)簡(jiǎn)單的實(shí)現(xiàn),還有很多不足的地方:
          1.對(duì)于Spring XML、MyBatis XML的支持。
          2.Instrumentation的局限性:由于jvm基于安全考慮,不允許改類結(jié)構(gòu),比如新增字段,新增方法和修改類的父類等。想要突破這種局限,就需要使用Dcevm(Java Hostspot的補(bǔ)?。┝恕?/span>
          歡迎有興趣的同學(xué)一起學(xué)習(xí)交流。


          瀏覽 64
          點(diǎn)贊
          評(píng)論
          收藏
          分享

          手機(jī)掃一掃分享

          分享
          舉報(bào)
          評(píng)論
          圖片
          表情
          推薦
          點(diǎn)贊
          評(píng)論
          收藏
          分享

          手機(jī)掃一掃分享

          分享
          舉報(bào)
          <kbd id="afajh"><form id="afajh"></form></kbd>
          <strong id="afajh"><dl id="afajh"></dl></strong>
            <del id="afajh"><form id="afajh"></form></del>
                1. <th id="afajh"><progress id="afajh"></progress></th>
                  <b id="afajh"><abbr id="afajh"></abbr></b>
                  <th id="afajh"><progress id="afajh"></progress></th>
                  国产婷婷一区二区 | av资源在线 | 青青草色在线 | a片视频在线播放 | 国产美女啪啪视频 |