-
Notifications
You must be signed in to change notification settings - Fork 3.8k
2019 07 14 基于JavaAgent的全链路监控五《ThreadLocal链路追踪》
作者:小傅哥
博客:https://bugstack.cn - 原创系列专题
沉淀、分享、成长,让自己和他人都能有所收获!
- 基于JavaAgent的全链路监控一《嗨!JavaAgent》
- 基于JavaAgent的全链路监控二《通过字节码增加监控执行耗时》
- 基于JavaAgent的全链路监控三《ByteBuddy操作监控方法字节码》
- 基于JavaAgent的全链路监控四《JVM内存与GC信息》
- 基于JavaAgent的全链路监控五《ThreadLocal链路追踪》
- 基于JavaAgent的全链路监控六《开发应用级监控》
Google开源的Dapper链路追踪组件,并在2010年发表了论文《Dapper, a Large-Scale Distributed Systems Tracing Infrastructure》,这篇文章是业内实现链路追踪的标杆和理论基础,具有非常大的参考价值。目前,链路追踪组件有Google的Dapper,Twitter 的Zipkin,以及阿里的Eagleeye (鹰眼)等,它们都是非常优秀的链路追踪开源组件。本文主要讲述如何在Spring Cloud Sleuth中集成Zipkin。在Spring Cloud Sleuth中集成Zipkin非常的简单,只需要引入相应的依赖和做相关的配置即可。
当业务程序代码在线上运行时,实例A、实例B、实例C,他们直接可能从上到下依次调用,为了能很好的监控程序的调用链路,我们需要对调用链路进行追踪监控。实例的外部可能是通过RPC、HTTP、SOCKET、WEBSERVICE等方式进行调用,内部是方法逻辑依次执行。外部例如http可以通过在头部写入追踪ID进行监控,内部使用threadlocal进行保存上下文关系。{ThreadLocal变量特殊的地方在于:对变量值的任何操作实际都是对这个变量在线程中的一份copy进行操作,不会影响另外一个线程中同一个ThreadLocal变量的值。}
- IntelliJ IDEA Community Edition
- jdk1.8.0_45 64位
- 配置位置:Run/Debug Configurations -> VM options
- 配置内容:-javaagent:E:\itstack\GIT\itstack.org\itstack-demo-agent\itstack-demo-agent-05\target\itstack-demo-agent-05-1.0.0-SNAPSHOT.jar=testargs
itstack-demo-agent-05
├── pom.xml
└── src
├── main
│ ├── java
│ │ └── org.itstack.demo.agent
│ │ ├──track
│ │ │ ├── TrackContext.java
│ │ │ └── TrackManager.java
│ │ ├── MyAdvice.java
│ │ └── MyAgent.java
│ └── resources
│ └── META-INF
│ └── MANIFEST.MF
└── test
└── java
└── org.itstack.demo.test
└── ApiTest.java
TrackContext.java
/**
* 博客:http://itstack.org
* 论坛:http://bugstack.cn
* 公众号:bugstack虫洞栈 {获取学习源码}
* create by fuzhengwei on 2019
*/
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);
}
}
TrackManager.java
/**
* 追踪管控
* 博客:http://itstack.org
* 论坛:http://bugstack.cn
* 公众号:bugstack虫洞栈 {获取学习源码}
* create by fuzhengwei on 2019
*/
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();
}
}
MyAdvice.java
/**
* 博客:http://itstack.org
* 论坛:http://bugstack.cn
* 公众号:bugstack虫洞栈 {获取学习源码}
* create by fuzhengwei on 2019
*/
public class MyAdvice {
@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();
}
}
MyAgent.java
/**
* javaagent
* 博客:http://itstack.org
* 论坛:http://bugstack.cn
* 公众号:bugstack虫洞栈 {获取学习源码}
* create by fuzhengwei on 2019
*/
public class MyAgent {
//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(MyAdvice.class)
.on(ElementMatchers.isMethod()
.and(ElementMatchers.any()).and(ElementMatchers.not(ElementMatchers.nameStartsWith("main")))));
return builder;
};
agentBuilder = agentBuilder.type(ElementMatchers.nameStartsWith("org.itstack.demo.test")).transform(transformer).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) {
}
}
MANIFEST.MF
Manifest-Version: 1.0
Premain-Class: org.itstack.demo.agent.MyAgent
Can-Redefine-Classes: true
ApiTest.java
/**
* 线程内方法追踪
* 博客:http://itstack.org
* 论坛:http://bugstack.cn
* 公众号:bugstack虫洞栈 {获取学习源码}
* create by fuzhengwei on 2019
* VM options:
* -javaagent:E:\itstack\GIT\itstack.org\itstack-demo-agent\itstack-demo-agent-05\target\itstack-demo-agent-05-1.0.0-SNAPSHOT.jar=testargs
*/
public class ApiTest {
public static void main(String[] args) {
//线程一
new Thread(() -> new ApiTest().http_lt1()).start();
//线程二
new Thread(() -> {
new ApiTest().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 org.itstack.demo.test.ApiTest
链路追踪:7dfd98e8-c474-461c-87b9-1da3bf6072c2 org.itstack.demo.test.ApiTest.http_lt1
测试结果:hi1
链路追踪:7dfd98e8-c474-461c-87b9-1da3bf6072c2 org.itstack.demo.test.ApiTest.http_lt2
测试结果:hi2
链路追踪:7dfd98e8-c474-461c-87b9-1da3bf6072c2 org.itstack.demo.test.ApiTest.http_lt3
测试结果:hi3
链路追踪:69cdf9d3-1f42-4cf6-8d80-5362dd7ea140 org.itstack.demo.test.ApiTest.http_lt1
测试结果:hi1
链路追踪:69cdf9d3-1f42-4cf6-8d80-5362dd7ea140 org.itstack.demo.test.ApiTest.http_lt2
测试结果:hi2
链路追踪:69cdf9d3-1f42-4cf6-8d80-5362dd7ea140 org.itstack.demo.test.ApiTest.http_lt3
测试结果:hi3
Process finished with exit code 0
上一篇:基于JavaAgent的全链路监控四《JVM内存与GC信息》
下一篇:基于JavaAgent的全链路监控六《开发应用级监控》
微信搜索「bugstack虫洞栈」公众号,关注后回复「基于JavaAgent的全链路监控」获取本文源码&更多原创专题案例!
小傅哥(微信:fustack),公众号:bugstack虫洞栈
| bugstack.cn - 沉淀、分享、成长,让自己和他人都能有所收获!
🌏 知识星球:码农会锁
实战项目:「DDD+RPC分布式抽奖系统
」、专属小册、问题解答、简历指导、架构图稿、视频课程
🐲 头条
-
💥
🎁 Lottery 抽奖系统
- 基于领域驱动设计的四层架构的互联网分布式开发实践 -
小傅哥的《重学 Java 设计模式》
- 全书彩印、重绘类图、添加内容 -
⭐小傅哥的《Java 面经手册》
- 全书5章29节,417页11.5万字,完稿&发版 -
小傅哥的《手撸 Spring》
- 通过带着读者手写简化版 Spring 框架,了解 Spring 核心原理 -
🌈小傅哥的《SpringBoot 中间件设计和开发》
- 小册16个中间件开发30个代码库
⛳ 目录
💋 精选
🐾 友链
建立本开源项目的初衷是基于个人学习与工作中对 Java 相关技术栈的总结记录,在这里也希望能帮助一些在学习 Java 过程中遇到问题的小伙伴,如果您需要转载本仓库的一些文章到自己的博客,请按照以下格式注明出处,谢谢合作。
作者:小傅哥
链接:https://bugstack.cn
来源:bugstack虫洞栈
2021年10月24日,小傅哥
的文章全部开源到代码库 CodeGuide
中,与同好同行,一起进步,共同维护。
这里我提供 3 种方式:
-
提出
Issue
:在 Issue 中指出你觉得需要改进/完善的地方(能够独立解决的话,可以在提出 Issue 后再提交PR
)。 -
处理
Issue
: 帮忙处理一些待处理的Issue
。 -
提交
PR
: 对于错别字/笔误这类问题可以直接提交PR
,无需提交Issue
确认。
详细参考:CodeGuide 贡献指南 - 非常感谢你的支持,这里会留下你的足迹
- 加群交流 本群的宗旨是给大家提供一个良好的技术学习交流平台,所以杜绝一切广告!由于微信群人满 100 之后无法加入,请扫描下方二维码先添加作者 “小傅哥” 微信(fustack),备注:加群。
- 公众号(bugstack虫洞栈) - 沉淀、分享、成长,专注于原创专题案例,以最易学习编程的方式分享知识,让自己和他人都能有所收获。
感谢以下人员对本仓库做出的贡献或者对小傅哥的赞赏,当然不仅仅只有这些贡献者,这里就不一一列举了。如果你希望被添加到这个名单中,并且提交过 Issue 或者 PR,请与我联系。