看完这篇文章我奶奶都懂Opentracing了(一)

news2024/12/27 13:07:41

前言

如果要基于Opentracing开发分布式链路追踪Java客户端工具包,首先肯定需要了解Opentracing中的各种概念,包括但不限于SpanScope等,其实这些概念在Opentracing的官方文档中是有比较详尽的说明的,英文不好也能靠着机器翻译读得通,但是读得通不代表读得懂,从来没有接触过分布式链路追踪的人就算把官方文档通读完,整体的概念还是显得比较抽象,所以本文作为Opentracing入门,旨在让从来没接触过分布式链路追踪的人也能理解Opentracing中的各种概念,为后续阅读相关源码和自行实现分布式链路追踪客户端工具包打好基础。

本文会从一个简单的例子入手,结合相关场景和源码实现,阐述Opentracing中的SpanScope等概念,通过阅读本文,可以快速了解关于分布式链路追踪的相关概念,并知道有哪些扩展点我们可以利用起来进行功能扩展。

Opentracingjaeger相关版本依赖如下。

opentracing-api版本:0.33.0
opentracing-spring-web版本:4.1.0
jaeger-client版本:1.8.1

正文

一. 场景演示

我们先抛开所有感情和先验知识,来看一个如下的客户端请求服务端的情况。

客户端只是简单的使用RestTemplate向服务端发起请求,请求在服务端会先通过filterChain,然后最终送到应用程序中提供的Controller

对于上述这样一个简单的场景,如果想要传递链路信息,我们先不考虑链路信息传递啥,我们首先确定一下链路信息放哪儿,毫无疑问,放在HTTP请求头中是侵入最小的。对于客户端而言,可以基于ClientHttpRequestInterceptor来为RestTemplate客户端提供统一的拦截器,拦截器的逻辑会在请求发起前被执行,我们可以在这个时候,把链路信息放在HTTP请求头中,对于服务端而言,可以注册一个过滤器,在过滤器中就可以从请求头里拿到链路信息,这样链路信息就从客户端传递到了服务端,下面就分别给出客户端和服务端的示例代码,我们通过这个示例,来了解如何使用基于Opentracing定义的api来传递链路信息,从而了解这个过程中出现的各种概念。

客户端这边的pom文件如下所示。

<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
    <modelVersion>4.0.0</modelVersion>

    <parent>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-parent</artifactId>
        <version>2.7.6</version>
    </parent>

    <groupId>com.learn.tracing.client</groupId>
    <artifactId>learn-tracing-client</artifactId>
    <version>1.0-SNAPSHOT</version>

    <properties>
        <maven.compiler.source>8</maven.compiler.source>
        <maven.compiler.target>8</maven.compiler.target>
    </properties>

    <dependencies>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-web</artifactId>
        </dependency>
        <dependency>
            <groupId>io.opentracing</groupId>
            <artifactId>opentracing-api</artifactId>
            <version>0.33.0</version>
        </dependency>
        <dependency>
            <groupId>io.opentracing.contrib</groupId>
            <artifactId>opentracing-spring-web</artifactId>
            <version>4.1.0</version>
        </dependency>

        <dependency>
            <groupId>io.jaegertracing</groupId>
            <artifactId>jaeger-client</artifactId>
            <version>1.8.1</version>
        </dependency>

        <dependency>
            <groupId>org.projectlombok</groupId>
            <artifactId>lombok</artifactId>
        </dependency>
    </dependencies>

</project>

客户端这边最重要的就是为RestTemplate提供的拦截器,对应实现如下所示。

public class RestTemplateTracingInterceptor implements ClientHttpRequestInterceptor {

    private final Tracer tracer;

    public RestTemplateTracingInterceptor(Tracer tracer) {
        this.tracer = tracer;
    }

    @NotNull
    public ClientHttpResponse intercept(@NotNull HttpRequest request, @NotNull byte[] body,
                                        ClientHttpRequestExecution execution) throws IOException {
        Span span = tracer.buildSpan(REST_TEMPLATE_SPAN_NAME)
                .withTag(Tags.SPAN_KIND.getKey(), Tags.SPAN_KIND_CLIENT)
                .start();
        span.setBaggageItem(REST_TEMPLATE_SPAN_TAG_URI, request.getURI().toString());
        tracer.inject(span.context(), Format.Builtin.HTTP_HEADERS, new HttpHeadersCarrier(request.getHeaders()));

        try (Scope scope = tracer.activateSpan(span)) {
            return execution.execute(request, body);
        } catch (IOException e) {
            Tags.ERROR.set(span, Boolean.TRUE);
            throw e;
        } finally {
            span.finish();
        }
    }

}

上面出现了很多陌生的内容例如SpanTracerTags,但是不慌,这些后面都会知道是啥,我们现在先把客户端和服务端搭起来。我们上述的拦截器使用了一个Tracer对象,那么我们就继续看一下这个Tracer对象的配置类是怎么写的,如下所示。

@Configuration
public class TracerConfig {

    @Autowired
    private SpanReporter spanReporter;

    @Autowired
    private Sampler sampler;

    @Bean
    public Tracer tracer() {
        return new JaegerTracer.Builder(TRACER_SERVICE_NAME)
                .withTraceId128Bit()
                .withSampler(sampler)
                .withReporter(spanReporter)
                .build();
    }

}

创建Tracer时指定了SpanReporterSampler,我们先不去深究SpanReporterSampler是啥,仅先看一下这两个bean的配置类,如下所示。

public class SpanReporter implements Reporter {

    public void report(JaegerSpan span) {
        
        
    }

    public void close() {

    }

}

@Configuration
public class ReporterConfig {

    @Bean
    public SpanReporter spanReporter() {
        return new SpanReporter();
    }

}

@Configuration
public class SamplerConfig {

    @Bean
    public Sampler sampler() {
        return new ProbabilisticSampler(DEFAULT_SAMPLE_RATE);
    }

}

到这里创建拦截器的相关内容已经全部给出,那么拦截器有了,还需要把拦截器设置给RestTemplate,所以再看一下RestTemplate的配置类,如下所示。

@Configuration
public class RestTemplateConfig {

    @Autowired
    private Tracer tracer;

    @Bean
    public RestTemplate restTemplate() {
        RestTemplate restTemplate = new RestTemplate();
        restTemplate.getInterceptors().add(new RestTemplateTracingInterceptor(tracer));
        return restTemplate;
    }

}

关于客户端最后的就是上述使用到的常量类以及一个测试的Controller,如下所示。

public class Constants {

    public static final String REST_TEMPLATE_SPAN_NAME = "RestTemplateSpan";
    public static final String REST_TEMPLATE_SPAN_TAG_URI = "uri";

    public static final String TRACER_SERVICE_NAME = "TracerService";

    public static final Double DEFAULT_SAMPLE_RATE = 1.0;

}

@RestController
public class TracingClientController {

    @Autowired
    private RestTemplate restTemplate;

    @GetMapping("/send")
    public void send(String url) {
        restTemplate.getForEntity(url, Void.class);
    }

}

客户端这边的工程目录结构如下图所示。

服务端的pom文件如下所示。

<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
    <modelVersion>4.0.0</modelVersion>

    <parent>
        <artifactId>spring-boot-starter-parent</artifactId>
        <groupId>org.springframework.boot</groupId>
        <version>2.7.6</version>
    </parent>

    <groupId>com.learn.tracing.server</groupId>
    <artifactId>learn-tracing-server</artifactId>
    <version>1.0-SNAPSHOT</version>

    <properties>
        <maven.compiler.source>8</maven.compiler.source>
        <maven.compiler.target>8</maven.compiler.target>
    </properties>

    <dependencies>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-web</artifactId>
        </dependency>
        <dependency>
            <groupId>io.opentracing</groupId>
            <artifactId>opentracing-api</artifactId>
            <version>0.33.0</version>
        </dependency>
        <dependency>
            <groupId>io.opentracing.contrib</groupId>
            <artifactId>opentracing-spring-web</artifactId>
            <version>4.1.0</version>
        </dependency>

        <dependency>
            <groupId>io.jaegertracing</groupId>
            <artifactId>jaeger-client</artifactId>
            <version>1.8.1</version>
        </dependency>

        <dependency>
            <groupId>org.projectlombok</groupId>
            <artifactId>lombok</artifactId>
        </dependency>
    </dependencies>

</project>

服务端这边最重要的是提供一个过滤器,如下所示。

public class TracingFilter implements Filter {

    private final Tracer tracer;

    public TracingFilter(Tracer tracer) {
        this.tracer = tracer;
    }

    @Override
    public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain)
            throws IOException, ServletException {
        HttpServletRequest request = (HttpServletRequest) servletRequest;
        HttpServletResponse response = (HttpServletResponse) servletResponse;

        SpanContext extractedSpanContext = tracer.extract(Format.Builtin.HTTP_HEADERS,
                new HttpServletRequestExtractAdapter(request));

        Span span = tracer.buildSpan(request.getMethod())
                .asChildOf(extractedSpanContext)
                .withTag(Tags.SPAN_KIND.getKey(), Tags.SPAN_KIND_SERVER)
                .start();

        response.setHeader(TRACE_ID_KEY, span.context().toTraceId());

        try (Scope scope = tracer.activateSpan(span)) {
            filterChain.doFilter(servletRequest, servletResponse);
        } catch (IOException | ServletException e) {
            Tags.ERROR.set(span, Boolean.TRUE);
            throw e;
        } finally {
            span.finish();
        }
    }

}

同样的使用到了Tracer,相关配置类如下所示。

@Configuration
public class TracerConfig {

    @Autowired
    private SpanReporter spanReporter;

    @Autowired
    private Sampler sampler;

    @Bean
    public Tracer tracer() {
        return new JaegerTracer.Builder(TRACER_SERVICE_NAME)
                .withTraceId128Bit()
                .withSampler(sampler)
                .withReporter(spanReporter)
                .build();
    }

}

SpanReporter以及SpanReporterSampler的配置类如下所示。

public class SpanReporter implements Reporter {

    public void report(JaegerSpan span) {
        
        
    }

    public void close() {

    }

}

@Configuration
public class ReporterConfig {

    @Bean
    public SpanReporter spanReporter() {
        return new SpanReporter();
    }

}

@Configuration
public class SamplerConfig {

    @Bean
    public Sampler sampler() {
        return new ProbabilisticSampler(DEFAULT_SAMPLE_RATE);
    }

}

现在还需要将TracingFilter注册到过滤器链中,主要是基于FilterRegistrationBean来完成注册,对应配置类如下所示。

@Configuration
public class ServletFilterConfig {

    @Bean
    public FilterRegistrationBean<TracingFilter> tracingFilter(Tracer tracer) {
        TracingFilter tracingFilter = new TracingFilter(tracer);
        FilterRegistrationBean<TracingFilter> filterFilterRegistrationBean
                = new FilterRegistrationBean<>(tracingFilter);
        filterFilterRegistrationBean.addUrlPatterns(ALL_URL_PATTERN_STR);
        return filterFilterRegistrationBean;
    }

}

关于服务端最后的就是上述使用到的常量类以及一个测试的Controller,如下所示。

public class Constants {

    public static final String TRACER_SERVICE_NAME = "TracerService";

    public static final Double DEFAULT_SAMPLE_RATE = 1.0;

    public static final String TRACE_ID_KEY = "traceId";

    public static final String ALL_URL_PATTERN_STR = "/*";

}

@RestController
public class TracingServerController {

    @GetMapping("/receive")
    public void send() {
        System.out.println("接收请求");
    }

}

服务端的工程目录结构如下所示。

最后,在将客户端运行在8081端口,服务端运行在8082端口,并调用如下接口。

http://localhost:8081/send?url=http://localhost:8082/receive

我们在客户端这边的RestTemplate的拦截器中,会通过Opentracing的相关api将请求URI放到请求头中,然后在服务端这边的TracingFilter中,又会从请求头中解析出客户端传递过来的URI,这一点可以分别在客户端RestTemplate的拦截器和服务端TracingFilter中打断点进行观察。

先看一下客户端使用RestTemplate发请求时,经过拦截器后,HTTP请求头中的字段,如下所示。

再看一下服务端这边在TracingFilter中解析出客户端传递过来的URI,如下所示。

也就是客户端这边通过Opentracingapi,将一些字段放在了请求头中,传递到了服务端。

那么问题就来了,我们自己不通过Opentracingapi,其实也是可以通过HTTP请求头来传递信息到下游,为啥要基于Opentracing呢,其实上述示例中,客户端除了传递请求URI到下游,还传递了在分布式链路中很重要的traceIdspanId,前者标记一次请求链路,后者代表这次请求链路上的节点,同时Opentracing还定义了如何让链路信息在进程中传递和跨进程传递,上述示例就是跨进程传递的一个简单演示。

好了到这里示例就演示完毕了,后续我们就基于上述的示例,以及相关的源码,来阐述Opentracing中的各种概念,为后面的分布式链路工具包开发奠定基础。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/1649733.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

【linux-IMX6ULL中断配置流程】

目录 1. Cortex-A7和GIC中断概述1. 1 Cortex-A7中断系统&#xff1a;1. 2 GIC中断控制器简介&#xff1a; 2. 中断配置概述3. 底层中断文件配置3.1 对启动文件.s的配置思路3.2 对中断函数配置思路 4. 上层中断配置流程 1. Cortex-A7和GIC中断概述 学习IMX6UL的中断处理系统&…

毕业就业信息|基于Springboot+vue的毕业就业信息管理系统的设计与实现(源码+数据库+文档)

毕业就业信息管理系统 目录 基于Springboot&#xff0b;vue的毕业就业信息管理系统设计与实现 一、前言 二、系统设计 三、系统功能设计 1学生信息管理 2 公司信息管理 3公告类型管理 4公告信息管理 四、数据库设计 五、核心代码 六、论文参考 七、最新计算机毕设…

自动化运维管理工具 Ansible-----【inventory 主机清单和playbook剧本】

目录 一、inventory 主机清单 1.1inventory 中的变量 1.1.1主机变量 1.1.2组变量 1.1.3组嵌套 二、Ansible 的脚本 ------ playbook&#xff08;剧本&#xff09; 2.1 playbook介绍 2.2playbook格式 2.3playbooks 的组成 2.4playbook编写 2.5运行playbook 2.5.1ans…

如何翻译外文文献【攻略】

如何翻译外文文献【攻略】 前言版权推荐如何翻译外文文献简单描述第一步 准备一篇外文文献第二步 翻译网站第三步 解锁文档第四步 编辑dpf第五步 pdf转为word第六步 编辑word方便操作快捷键设置章节设置页眉页脚设置页码三线表的绘制 第七步 word转为pdf 最后 前言 2024-5-7 1…

电脑提示“无法定位程序输入点kernel32.dll”怎么解决?分享kernel32.dll丢失的五个修复方法

打开游戏或者软件的时候&#xff0c;电脑提示由于找不到kernel32.dll&#xff0c;无法继续执行此代码怎么办&#xff0c;其实修复起来不难。首先需要先知道怎么是dll文件&#xff0c;dll文件可以简单的把库文件看成一种代码仓库&#xff0c;它提供给使用者一些可以直接拿来用的…

【SSM进阶学习系列丨分页篇】PageHelper 分页插件集成实践

文章目录 一、说明什么是分页PageHelper介绍 二、导入依赖三、集成Spring框架中四、编写Service五、编写Controller六、编写queryAllByPage页面展示数据 一、说明 什么是分页 ​ 针对分页&#xff0c;使用的是PageHelper分页插件&#xff0c;版本使用的是5.1.8 。 ​ 参考文档…

openEuler 22.03 GPT分区表模式下磁盘分区管理

目录 GPT分区表模式下磁盘分区管理parted交互式创建分区步骤 1 执行如下步骤对/dev/sdc磁盘分区 非交互式创建分区步骤 1 输入如下命令直接创建分区。 删除分区步骤 1 执行如下命令删除/dev/sdc1分区。 GPT分区表模式下磁盘分区管理 parted交互式创建分区 步骤 1 执行如下步骤…

Day 24 数据库管理及数据类型

数据库管理及数据类型 一&#xff1a;数据类型 1.数值类型 整数类型 ​ 整数类型&#xff1a;TINYINT SMALLINT MEDIUMINT INT BIGINT ​ 作用&#xff1a;用于存储用户的年龄、游戏的Level、经验值等 浮点数类型 ​ 浮点数类型&#xff1a;FLOAT DOUBLE ​ 作用&#xf…

2024 cleanmymac有没有必要买呢,全反面分析

在使用mac时&#xff0c;小编遇到了运行内存不足、硬盘空间不足的情况。遇到这种情况&#xff0c;我们可以借助经典的电脑深度清理软件——CleanMyMac X&#xff0c;清理不常用的软件和系统垃圾&#xff0c;非常好用&#xff01;不过&#xff0c;有许多网友发现CleanMyMac X有免…

基于Springboot的校园新闻管理系统(有报告)。Javaee项目,springboot项目。

演示视频&#xff1a; 基于Springboot的校园新闻管理系统&#xff08;有报告&#xff09;。Javaee项目&#xff0c;springboot项目。 项目介绍&#xff1a; 采用M&#xff08;model&#xff09;V&#xff08;view&#xff09;C&#xff08;controller&#xff09;三层体系结构…

NFTScan 与 Scattering 达成合作伙伴,双方将共同解决混合 NFT 数据需求

在区块链领域&#xff0c;NFT 的创新与发展从未停止。近日&#xff0c;NFT 数据基础设施 NFTScan 与一站式混合 NFT 交易市场 Scattering 达成合作伙伴关系&#xff0c;双方将在 NFT 数据层面展开合作&#xff0c;共同解决混合 NFT 的独特数据需求&#xff0c;Scattering 在协议…

非平衡数据处理-Tomek link算法介绍,代码和实战测评

作者Toby&#xff0c;来源公众号&#xff1a;Python风控模型&#xff0c;非平衡数据处理-Tomek link算法 概述 非平衡数据在金融风控领域、反欺诈客户识别、广告智能推荐和生物医疗中普遍存在。一般而言&#xff0c;不平衡数据正负样本的比例差异极大&#xff0c;如在Kaggle竞…

Quora 首席执行官亚当·德安杰洛 (Adam D’Angelo) 谈论了 AI、聊天机器人平台 Poe,以及 OpenAI 为什么不是竞争对手

每周跟踪AI热点新闻动向和震撼发展 想要探索生成式人工智能的前沿进展吗&#xff1f;订阅我们的简报&#xff0c;深入解析最新的技术突破、实际应用案例和未来的趋势。与全球数同行一同&#xff0c;从行业内部的深度分析和实用指南中受益。不要错过这个机会&#xff0c;成为AI领…

无线通信基础

这里写目录标题 通信概述什么是无线通信无线通信电磁波 通信概述 什么是无线通信 无线通信 : 是指利用电磁波信号可以在自由空间中传播的特性进行信息交换的一种通信方式 无线通信的关键技术包括调制技术、解调技术、信道编码技术、信号处理技术、天线技术等。这些技术的不断…

【探索Java编程:从入门到入狱】Day4

&#x1f36c; 博主介绍&#x1f468;‍&#x1f393; 博主介绍&#xff1a;大家好&#xff0c;我是 hacker-routing &#xff0c;很高兴认识大家~ ✨主攻领域&#xff1a;【渗透领域】【应急响应】 【Java、PHP】 【VulnHub靶场复现】【面试分析】 &#x1f389;点赞➕评论➕收…

CANdela/Diva系列2--CANdela Studio的工作树介绍1

本系列的第一篇文章&#xff08;CANdela/Diva系列1--CANdela Studio的基本介绍&#xff09;主要介绍了CANdela这个工具&#xff0c;本篇文章将对CANdela Studio的工作树的每个模块进行详细介绍&#xff0c;不啰嗦&#xff0c;直接开始&#xff01; 目录 1. ECU Information的…

数据库被Elbie勒索病毒加密可以恢复吗?

一、Elbie勒索病毒简介 Elbie勒索病毒是一种严重的网络安全威胁&#xff0c;它通过加密用户文件并索要高额赎金来获取解密密钥。该病毒通常通过电子邮件附件、恶意网站、社交媒体平台以及利用用户网站服务器上的漏洞进行传播。一旦感染&#xff0c;用户的文件将被加密&#xff…

【vulhub靶场】Tomcat中间件漏洞复现

【vulhub靶场】Tomcat中间件漏洞复现 一、Tomcat AJP 任意文件读取/包含漏洞 &#xff08;CVE-2020-1938&#xff09;1. 漏洞描述2. 影响版本3. 漏洞原理4. 漏洞复现 二、任意文件写入漏洞 &#xff08;CVE-2017-12615&#xff09;1. 漏洞原理2. 影响版本3. 漏洞复现 三、Tomca…

.[[MyFile@waifu.club]].svh勒索病毒数据库恢复方案

.[[MyFilewaifu.club]].svh勒索病毒有什么特点&#xff1f; .[[MyFilewaifu.club]].svh是一种最近多发的勒索病毒&#xff0c;它通过加密受害者的文件并要求支付赎金来解锁&#xff0c;从而达到勒索钱财的目的。恢复重要数据请添加技术服务号(safe130)。以下是关于这种病毒的详…