基于JavaAgent的全链路监控五《ThreadLocal链路追踪》

本文涉及的产品
应用实时监控服务-可观测链路OpenTelemetry版,每月50GB免费额度
简介: Google开源的Dapper链路追踪组件,并在2010年发表了论文《Dapper, a Large-Scale Distributed Systems Tracing Infrastructure》,这篇文章是业内实现链路追踪的标杆和理论基础,具有非常大的参考价值。目前,链路追踪组件有Google的Dapper,Twitter 的Zipkin,以及阿里的Eagleeye (鹰眼)等,它们都是非常优秀的链路追踪开源组件。本文主要讲述如何在Spring Cloud Sleuth中集成Zipkin。在Spring Cloud Sleuth中集成Zipkin非常的简单,只需要引入相应的依赖和做相关的

10.jpg

案例简述

Google开源的Dapper链路追踪组件,并在2010年发表了论文《Dapper, a Large-Scale Distributed Systems Tracing Infrastructure》,这篇文章是业内实现链路追踪的标杆和理论基础,具有非常大的参考价值。目前,链路追踪组件有Google的Dapper,Twitter 的Zipkin,以及阿里的Eagleeye (鹰眼)等,它们都是非常优秀的链路追踪开源组件。本文主要讲述如何在Spring Cloud Sleuth中集成Zipkin。在Spring Cloud Sleuth中集成Zipkin非常的简单,只需要引入相应的依赖和做相关的配置即可。

11.jpg

链路追踪(Dapper)


当业务程序代码在线上运行时,实例A、实例B、实例C,他们直接可能从上到下依次调用,为了能很好的监控程序的调用链路,我们需要对调用链路进行追踪监控。实例的外部可能是通过RPC、HTTP、SOCKET、WEBSERVICE等方式进行调用,内部是方法逻辑依次执行。外部例如http可以通过在头部写入追踪ID进行监控,内部使用threadlocal进行保存上下文关系。{ThreadLocal变量特殊的地方在于:对变量值的任何操作实际都是对这个变量在线程中的一份copy进行操作,不会影响另外一个线程中同一个ThreadLocal变量的值。}

环境准备

1、IntelliJ IDEA Community Edition

2、jdk1.8.0_45 64位

配置信息

(路径相关修改为自己的)

1、配置位置:Run/Debug Configurations -> VM options

2、配置内容:-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
相关实践学习
基于OpenTelemetry构建全链路追踪与监控
本实验将带领您快速上手可观测链路OpenTelemetry版,包括部署并接入多语言应用、体验TraceId自动注入至日志以实现调用链与日志的关联查询、以及切换调用链透传协议以满足全链路打通的需求。
分布式链路追踪Skywalking
Skywalking是一个基于分布式跟踪的应用程序性能监控系统,用于从服务和云原生等基础设施中收集、分析、聚合以及可视化数据,提供了一种简便的方式来清晰地观测分布式系统,具有分布式追踪、性能指标分析、应用和服务依赖分析等功能。 分布式追踪系统发展很快,种类繁多,给我们带来很大的方便。但在数据采集过程中,有时需要侵入用户代码,并且不同系统的 API 并不兼容,这就导致了如果希望切换追踪系统,往往会带来较大改动。OpenTracing为了解决不同的分布式追踪系统 API 不兼容的问题,诞生了 OpenTracing 规范。OpenTracing 是一个轻量级的标准化层,它位于应用程序/类库和追踪或日志分析程序之间。Skywalking基于OpenTracing规范开发,具有性能好,支持多语言探针,无侵入性等优势,可以帮助我们准确快速的定位到线上故障和性能瓶颈。 在本套课程中,我们将全面的讲解Skywalking相关的知识。从APM系统、分布式调用链等基础概念的学习加深对Skywalking的理解,从0开始搭建一套完整的Skywalking环境,学会对各类应用进行监控,学习Skywalking常用插件。Skywalking原理章节中,将会对Skywalking使用的agent探针技术进行深度剖析,除此之外还会对OpenTracing规范作整体上的介绍。通过对本套课程的学习,不止能学会如何使用Skywalking,还将对其底层原理和分布式架构有更深的理解。本课程由黑马程序员提供。
目录
相关文章
|
3月前
|
存储 监控 开发者
分布式链路监控系统问题之系统拆分后链路追踪技术的问题如何解决
分布式链路监控系统问题之系统拆分后链路追踪技术的问题如何解决
|
存储 监控 数据可视化
Golang链路追踪:实现高效可靠的分布式系统监控
Golang链路追踪:实现高效可靠的分布式系统监控
|
SQL 缓存 运维
使用篇丨链路追踪(Tracing)很简单:链路实时分析、监控与告警
使用篇丨链路追踪(Tracing)很简单:链路实时分析、监控与告警
6552 11
使用篇丨链路追踪(Tracing)很简单:链路实时分析、监控与告警
|
SQL 缓存 运维
使用篇丨链路追踪(Tracing)很简单:链路拓扑
使用篇丨链路追踪(Tracing)很简单:链路拓扑
31531 8
|
存储 缓存 运维
进阶篇丨链路追踪(Tracing)很简单:链路成本指南
进阶篇丨链路追踪(Tracing)很简单:链路成本指南
|
存储 监控 NoSQL
【微服务】分布式如何利用Skywalking实现链路追踪与监控?
微服务下的分布式如何实现链路追踪和监控。
986 1
【微服务】分布式如何利用Skywalking实现链路追踪与监控?
|
SQL 缓存 运维
链路追踪(Tracing)其实很简单——链路实时分析、监控与告警
统计分析是我们观察、应用分布式链路追踪技术的重要手段。我们既可以根据不同场景要求进行实时的后聚合分析,也可以将常用的分析语句固化成规则生成预聚合指标,实现常态化监控与告警。相对于链路多维筛选,统计分析需要明确分析对象与聚合维度。其中,分析对象决定了我们对哪些指标进行聚合操作,比如请求量、耗时或错误率。而聚合维度决定了我们对哪些特征进行统计对比,比如不同应用、接口、IP、用户类型的统计量对比。接下来,我们先了解下分析对象和聚合维度的具体概念,再介绍实时分析与监控告警的具体用法。
3887 0
链路追踪(Tracing)其实很简单——链路实时分析、监控与告警
|
Kubernetes 监控 Java
SpringCloud链路追踪实时流量最佳解决方案:Pinpoint分布式链路追踪、链路监控平台详细搭建步骤,SpringBoot/SpringCloud微服务注册到Pinpoint(2022年最新)
SpringCloud链路追踪实时流量最佳解决方案:Pinpoint分布式链路追踪、链路监控平台详细搭建步骤,SpringBoot/SpringCloud微服务注册到Pinpoint(2022年最新)
1717 0
SpringCloud链路追踪实时流量最佳解决方案:Pinpoint分布式链路追踪、链路监控平台详细搭建步骤,SpringBoot/SpringCloud微服务注册到Pinpoint(2022年最新)
|
SQL Arthas 运维
链路追踪(Tracing)其实很简单——多维链路筛选
在使用调用链分析问题之前,还有一个很重要的步骤,就是从海量链路数据中,通过各种条件筛选出真实反应当前问题的调用链,这个动作就叫做链路筛选。那什么叫多维呢?多维是指通过 TraceId、链路特征或自定义标签等多种维度进行链路筛选。每一种筛选条件都是由日常开发/运维的场景演变而来,最为契合当下的使用方式,提高了链路筛选的效率和精准度。
1645 0
链路追踪(Tracing)其实很简单——多维链路筛选
|
消息中间件 Prometheus 监控
监控、链路追踪、日志的区别,傻傻分不清?
对于一个系统来说,监控、链路追踪、日志的这三者需求都是必然存在的,而有的时候我们会搞不清楚这三者相互之间是什么关系。 我之前在做系统设计的时候也考虑过,是不是有必要引入那么多组件,毕竟如果这三者完全分开每一个一项的话,就有三个组件了(事实上就是:Prometheus+Grafana、Jaeger、ELK)。
636 0

热门文章

最新文章

下一篇
无影云桌面