赞
踩
转载:基于JavaAgent的全链路监控五《ThreadLocal链路追踪》
Google开源的Dapper链路追踪组件,并在2010年发表了论文《Dapper, a Large-Scale Distributed Systems Tracing Infrastructure》,这篇文章是业内实现链路追踪的标杆和理论基础,具有非常大的参考价值。目前,链路追踪组件有Google的Dapper,Twitter 的Zipkin,以及阿里的Eagleeye (鹰眼)等,它们都是非常优秀的链路追踪开源组件。本文主要讲述如何在Spring Cloud Sleuth中集成Zipkin。在Spring Cloud Sleuth中集成Zipkin非常的简单,只需要引入相应的依赖和做相关的配置即可。
链路追踪Dapper 当业务程序代码在线上运行时,实例A、实例B、实例C,他们直接可能从上到下依次调用,为了能很好的监控程序的调用链路,我们需要对调用链路进行追踪监控。实例的外部可能是通过RPC、HTTP、SOCKET、WEBSERVICE等方式进行调用,内部是方法逻辑依次执行。外部例如http可以通过在头部写入追踪ID进行监控,内部使用threadlocal进行保存上下文关系。{ThreadLocal变量特殊的地方在于:对变量值的任何操作实际都是对这个变量在线程中的一份copy进行操作,不会影响另外一个线程中同一个ThreadLocal变量的值。}
IntelliJ IDEA Community Edition 2019
jdk1.8.0_45 64位
配置位置:Run/Debug Configurations -> VM options
配置内容:-javaagent:-javaagent:/Users/lcc/IdeaProjects/lcc_work/test-byte-buddy/byte-buddy-v1x-javaagent-demo3/target/byte-buddy-v1x-javaagent-demo3-1.0-SNAPSHOT.jar=test
<properties> <asm.version>9.0</asm.version> <slf4j-api.version>1.7.28</slf4j-api.version> <byte-buddy.version>1.12.11</byte-buddy.version> <!-- <byte-buddy.version>1.8.20</byte-buddy.version>--> <fastjson.version>1.2.76</fastjson.version> <lombok.version>1.18.16</lombok.version> </properties> <dependencies> <!-- 日志 相关 --> <dependency> <groupId>org.slf4j</groupId> <artifactId>slf4j-api</artifactId> <version>${slf4j-api.version}</version> </dependency> <dependency> <groupId>org.slf4j</groupId> <artifactId>slf4j-log4j12</artifactId> <version>${slf4j-api.version}</version> </dependency> <dependency> <groupId>com.alibaba</groupId> <artifactId>fastjson</artifactId> <version>${fastjson.version}</version> </dependency> <dependency> <groupId>org.projectlombok</groupId> <artifactId>lombok</artifactId> <version>${lombok.version}</version> </dependency> <dependency> <groupId>net.bytebuddy</groupId> <artifactId>byte-buddy</artifactId> <version>${byte-buddy.version}</version> </dependency> <dependency> <groupId>net.bytebuddy</groupId> <artifactId>byte-buddy-agent</artifactId> <version>${byte-buddy.version}</version> </dependency> <dependency> <groupId>com.bytebuddy</groupId> <artifactId>byte-buddy-commons</artifactId> <version>1.0-SNAPSHOT</version> </dependency> </dependencies> <build> <plugins> <plugin> <groupId>org.apache.maven.plugins</groupId> <artifactId>maven-compiler-plugin</artifactId> <configuration> <source>1.8</source> <target>1.8</target> <encoding>utf-8</encoding> </configuration> </plugin> <plugin> <groupId>org.apache.maven.plugins</groupId> <artifactId>maven-jar-plugin</artifactId> <configuration> <archive> <manifestEntries> <addClasspath>true</addClasspath> <!-- <Premain-Class>com.javaagent.bytebuddy.MyPreMainAgent</Premain-Class>--> <!-- <Premain-Class>com.javaagent.bytebuddy.demo.MyAgent</Premain-Class>--> <Premain-Class>com.javaagent.bytebuddy.demo4.TraceAgent</Premain-Class> <addDefaultImplementationEntries>true</addDefaultImplementationEntries> <addDefaultSpecificationEntries>true</addDefaultSpecificationEntries> <Can-Redefine-Classes>true</Can-Redefine-Classes> <Can-Retransform-Classes>true</Can-Retransform-Classes> </manifestEntries> </archive> </configuration> </plugin> </plugins> </build>
TrackManager 设置如下
package com.javaagent.bytebuddy.demo4; import java.util.Stack; public class TrackManager { private static final ThreadLocal<Stack<String>> track = new ThreadLocal<Stack<String>>(); private static String createSpan() { Stack<String> stack = track.get(); if (stack == null) { stack = new Stack<>(); track.set(stack); } String linkId; if (stack.isEmpty()) { linkId = TrackContext.getLinkId(); if (linkId == null) { linkId = "nvl"; TrackContext.setLinkId(linkId); } } else { linkId = stack.peek(); TrackContext.setLinkId(linkId); } return linkId; } public static String createEntrySpan() { String span = createSpan(); Stack<String> stack = track.get(); stack.push(span); return span; } public static String getExitSpan() { Stack<String> stack = track.get(); if (stack == null || stack.isEmpty()) { TrackContext.clear(); return null; } return stack.pop(); } public static String getCurrentSpan() { Stack<String> stack = track.get(); if (stack == null || stack.isEmpty()) { return null; } return stack.peek(); } }
TrackContext设置如下
package com.javaagent.bytebuddy.demo4; /** * @author lcc */ public class TrackContext { private static final ThreadLocal<String> trackLocal = new ThreadLocal<String>(); public static void clear(){ trackLocal.remove(); } public static String getLinkId(){ return trackLocal.get(); } public static void setLinkId(String linkId){ trackLocal.set(linkId); } }
TraceAdvice设置如下
package com.javaagent.bytebuddy.demo4; import net.bytebuddy.asm.Advice; import java.util.UUID; public class TraceAdvice { @Advice.OnMethodEnter() public static void enter(@Advice.Origin("#t") String className, @Advice.Origin("#m") String methodName) { String linkId = TrackManager.getCurrentSpan(); if (null == linkId) { linkId = UUID.randomUUID().toString(); TrackContext.setLinkId(linkId); } String entrySpan = TrackManager.createEntrySpan(); System.out.println("链路追踪:" + entrySpan + " " + className + "." + methodName); } @Advice.OnMethodExit() public static void exit(@Advice.Origin("#t") String className, @Advice.Origin("#m") String methodName) { TrackManager.getExitSpan(); } }
TraceAgent设置如下
package com.javaagent.bytebuddy.demo4; import net.bytebuddy.agent.builder.AgentBuilder; import net.bytebuddy.asm.Advice; import net.bytebuddy.description.NamedElement; import net.bytebuddy.description.type.TypeDescription; import net.bytebuddy.dynamic.DynamicType; import net.bytebuddy.matcher.ElementMatcher; import net.bytebuddy.matcher.ElementMatchers; import net.bytebuddy.utility.JavaModule; import java.lang.instrument.Instrumentation; public class TraceAgent { //JVM 首先尝试在代理类上调用以下方法 public static void premain(String agentArgs, Instrumentation inst) { System.out.println("基于javaagent链路追踪"); AgentBuilder agentBuilder = new AgentBuilder.Default(); AgentBuilder.Transformer transformer = (builder, typeDescription, classLoader, javaModule) -> { builder = builder.visit( Advice.to(TraceAdvice.class) .on(ElementMatchers.isMethod() /** * 测试发现,这里需要所有,不能做下面的改造 否则没有相关的效果 */ .and(ElementMatchers.any()) // 这里相比原来的做了改造 我们只看我们这个包下的 // .and(ElementMatchers.nameStartsWith("com.javaagent.bytebuddy.demo4_1")) .and(ElementMatchers.not(ElementMatchers.nameStartsWith("main"))))); return builder; }; /** * 这里要写我们的目标类,不能包括自身 TraceAgent 所在包,否则会报错如下 * "Thread-0" java.lang.StackOverflowError * at com.javaagent.bytebuddy.demo4.TrackManager.getCurrentSpan(TrackManager.java:47) * */ ElementMatcher.Junction<NamedElement> namedElementJunction = ElementMatchers.nameStartsWith("com.javaagent.bytebuddy.demo4_1"); AgentBuilder.Identified.Extendable transform = agentBuilder.type(namedElementJunction).transform(transformer); // 1.12.11 版本使用这个 agentBuilder = transform.asTerminalTransformation(); // 1.8.20 版本使用这个 // agentBuilder = transform.asDecorator(); //监听 AgentBuilder.Listener listener = new AgentBuilder.Listener() { @Override public void onDiscovery(String s, ClassLoader classLoader, JavaModule javaModule, boolean b) { } @Override public void onTransformation(TypeDescription typeDescription, ClassLoader classLoader, JavaModule javaModule, boolean b, DynamicType dynamicType) { System.out.println("onTransformation:" + typeDescription); } @Override public void onIgnored(TypeDescription typeDescription, ClassLoader classLoader, JavaModule javaModule, boolean b) { } @Override public void onError(String s, ClassLoader classLoader, JavaModule javaModule, boolean b, Throwable throwable) { } @Override public void onComplete(String s, ClassLoader classLoader, JavaModule javaModule, boolean b) { } }; agentBuilder.with(listener).installOn(inst); } //如果代理类没有实现上面的方法,那么 JVM 将尝试调用该方法 public static void premain(String agentArgs) { } }
这里注意测试类的包名称和主要类的名称不一致,不在一个包下,否则会报错
package com.javaagent.bytebuddy.demo4_1; public class TraceAgentTest { public static void main(String[] args) { //线程一 new Thread(() -> new TraceAgentTest().http_lt1()).start(); //线程二 new Thread(() -> { new TraceAgentTest().http_lt1(); }).start(); } public void http_lt1() { System.out.println("测试结果:hi1"); http_lt2(); } public void http_lt2() { System.out.println("测试结果:hi2"); http_lt3(); } public void http_lt3() { System.out.println("测试结果:hi3"); } }
基于javaagent链路追踪
onTransformation:class com.javaagent.bytebuddy.demo4_1.TraceAgentTest
链路追踪:d9192c02-0fc7-437c-a47f-c150361dca19 com.javaagent.bytebuddy.demo4_1.TraceAgentTest.http_lt1
测试结果:hi1
链路追踪:7fde1ae3-95a0-4a34-a0d6-06fcbe0e596e com.javaagent.bytebuddy.demo4_1.TraceAgentTest.http_lt1
测试结果:hi1
链路追踪:d9192c02-0fc7-437c-a47f-c150361dca19 com.javaagent.bytebuddy.demo4_1.TraceAgentTest.http_lt2
测试结果:hi2
链路追踪:d9192c02-0fc7-437c-a47f-c150361dca19 com.javaagent.bytebuddy.demo4_1.TraceAgentTest.http_lt3
测试结果:hi3
链路追踪:7fde1ae3-95a0-4a34-a0d6-06fcbe0e596e com.javaagent.bytebuddy.demo4_1.TraceAgentTest.http_lt2
测试结果:hi2
链路追踪:7fde1ae3-95a0-4a34-a0d6-06fcbe0e596e com.javaagent.bytebuddy.demo4_1.TraceAgentTest.http_lt3
测试结果:hi3
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。