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

前言

如果要基于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;}@NotNullpublic 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 {@Autowiredprivate SpanReporter spanReporter;@Autowiredprivate Sampler sampler;@Beanpublic 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 {@Beanpublic SpanReporter spanReporter() {return new SpanReporter();}}@Configuration
public class SamplerConfig {@Beanpublic Sampler sampler() {return new ProbabilisticSampler(DEFAULT_SAMPLE_RATE);}}

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

@Configuration
public class RestTemplateConfig {@Autowiredprivate Tracer tracer;@Beanpublic 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 {@Autowiredprivate 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;}@Overridepublic 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 {@Autowiredprivate SpanReporter spanReporter;@Autowiredprivate Sampler sampler;@Beanpublic 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 {@Beanpublic SpanReporter spanReporter() {return new SpanReporter();}}@Configuration
public class SamplerConfig {@Beanpublic Sampler sampler() {return new ProbabilisticSampler(DEFAULT_SAMPLE_RATE);}}

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

@Configuration
public class ServletFilterConfig {@Beanpublic 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://xiahunao.cn/news/3017269.html

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

相关文章

【无标题】程序设计和c语言-谭浩强配套(适合专升本)

一晃大半年没更新了&#xff0c;一直在备考&#xff0c;想着这几天把前段时间学的c语言给大家分享一下&#xff0c;在此做了一个专栏&#xff0c;有需要的小伙伴可私信获取。 说明&#xff1a;本专栏所有内容皆适合专升本复习资料&#xff0c;本人手上也有日常刷题整理的错题以…

LT6911GXC HDMI2.1转mipi / lvds 支持8K 60HZ,提供技术支持

一、 HDMI 以及 GPIO 使用的相关配置 1.1 配置 EDID 在 LT6911GX.c 文件中添加需要的 EDID 二、输出信号配置 2.1 选择输出信号格式 在 Global.h 中配置输出信号类型&#xff0c;如下图所示 2.2 MIPI 信号格式配置 在 MIPITXGlobal.h 中配置 MIPI 相关参数

JAVA面向对象高级部分

内部类 内部类的四种形式 内部类概述、成员内部类 代码示例 创建对象的格式 通过对象名访问内部类方法 若内外部类的成员变量名冲突&#xff0c;如何在内部类分别访问外部成员变量。 总结 静态内部类 代码示例 访问静态内部类的方法 不能在静态内部类中访问实例成员变量 …

初识C++ · 类和对象(下)

目录 1 再谈构造函数 2 类中的隐式类型转换 3 Static成员 4 友元和内部类 5 匿名对象 6 编译器的一些优化 1 再谈构造函数 先看一段代码&#xff1a; class Date { public :Date(int year, int month, int day){_year year;_month month;_day day;} private:int _ye…

C#修改默认参数settings文件

右击项目在设置中进行修改&#xff1a; 千万不要在这里改。 如果要在自己的项目里添加这个文件&#xff0c;首先新建个文件夹&#xff0c;然后添加.setting文件&#xff0c;然后再像上面说的那样添加属性。

Vue3专栏项目 -- 一、第一个页面(上)

一、ColumnList 组件&#xff08;专栏列表组件&#xff09;编码&#xff1a; 该组件要接收一个数组&#xff0c;数组中是一个个专栏数据&#xff0c;数据中包括id、title、avator、description。所以我们定义一个泛型&#xff0c;泛型为id为number类型title为string类型如下这…

明火检测实时识别报警:视觉算法助力安全生产管理

背景与现状 在各种工作、生产环境下&#xff0c;明火的存在往往是潜在的安全隐患。无论是加油站、化工园区、仓储场所还是校园&#xff0c;明火一旦失控就会引发火灾&#xff0c;造成严重的人员伤亡和财产损失。传统的明火检查手段主要依赖于人工巡查和定期的消防检查&#xf…

shell脚本脚本变量

shell脚本的概念&#xff1a; 1.讲要执行的命令按顺序保存到一个文本文件 2.给文件可执行权限 3.可以结合各种shell控制语句以完成更复杂的操作 linux中包含shell的文件有 [rootlocalhost ~]# cat /etc/shells /bin/sh #UNIX最初使用的 shell&#xff0c;已经被…

Type-C转音频(USB2.0数据传输)+PD充电芯片乐得瑞LDR6500/LDR6023

LDR6500 USB-C DRP 接口 USB PD 通信芯片概述 Type-C转音频(USB2.0数据传输)PD充电芯片乐得瑞LDR6500LDR6500是乐得瑞科技针对USB Type-C标准中的Bridge设备而开发的USB-C DRP&#xff08;Dual Role Port&#xff0c;双角色端口&#xff09;接口USB PD&#xff08;Power Deliv…

选择适用的无尘棉签:保障洁净生产环境下的高效擦拭

随着洁净生产条件的日益普及和无尘级别要求的提高&#xff0c;无尘擦拭用品成为广大用户追捧的必备工具。在这个领域&#xff0c;无尘棉签作为一种高效的擦拭工具&#xff0c;扮演着重要的角色。然而&#xff0c;面对市场上种类繁多的无尘棉签&#xff0c;如何选择最合适的产品…

【FL常用插件#1】Ozone11臭氧的安装和使用

本文内容收集自互联网&#xff0c;仅供个人学习参考使用&#xff0c;不允许用于商业用途&#xff0c;造成的侵权行为与本文作者无关 安装 VST2、VST3、AAX和NKS是音频技术界常见的几种插件格式&#xff0c;它们在功能和兼容性上有所不同&#xff1a; VST2 (Virtual Studio Tec…

46. UE5 RPG 实现角色死亡效果

在上一篇文章中&#xff0c;我们实现了敌人受到攻击后会播放受击动画&#xff0c;并且还给角色设置了受击标签。并在角色受击时&#xff0c;在角色身上挂上受击标签&#xff0c;在c里&#xff0c;如果挂载了此标签&#xff0c;速度将降为0 。 受击有了&#xff0c;接下来我们将…

element-plus el-time-picker 时间段选择(可多选)

实现一个如图的时间段选择器 处理好时间回显逻辑&#xff0c;组件内[‘’,‘’],后端数据[{startTime:‘’,endTime:‘’}]处理好加和减的显示逻辑 <template><div><div v-for"(item, index) in currentChoose" :key"index" class"fl…

STM32G0存储器和总线架构

文章目录 前言一、系统架构二、存储器构成三、存储器地址映射四、存储器边界地址五、外设寄存器边界地址 前言 此文章是STM32G0 MCU的学习记录&#xff0c;并非权威&#xff0c;请谨慎参考。 STM32G0主流微控制器基于工作频率可达64 MHz的高性能Arm Cortex-M0 32位RISC内核。该…

Day_2

1. 菜品管理 新增菜品 接口设计 1. 根据类型查询分类&#xff08;分类管理已完成&#xff09; 查看接口文档即可 2. 文件上传 创建Bucket 采用的是阿里云的OSS对象存储服务 新增AccessKey 3. 菜品的新增逻辑 代码开发 1. 文件上传接口开发 为了提高代码的解耦性&#…

【强训笔记】day11

NO.1 思路&#xff1a;枚举&#xff0c;设一号大礼包的数量为x&#xff0c;二号大礼包的数量为y&#xff0c;用循环枚举一号大礼包的个数得到二号大礼包的数量&#xff0c;使得某一时刻axby的值最大。 代码实现&#xff1a; #include<iostream>using namespace std;lo…

昂科烧录器支持Infineon英飞凌的三相电机驱动器TLE9877QXA40

芯片烧录行业领导者-昂科技术近日发布最新的烧录软件更新及新增支持的芯片型号列表&#xff0c;其中Infineon英飞凌的三相电机驱动器TLE9877QXA40已经被昂科的通用烧录平台AP8000所支持。 TLE9877QXA40是一款单芯片三相电机驱动器&#xff0c;集成了行业标准的ARMCortex™M3 内…

如何去官网下载windows10操作系统iso镜像

文章目录 一、先从微软中国官网https://www.microsoft.com/zh-cn/进去二、然后按图示一步步点进去三、点击下载工具这个工具会帮你生成windows操作系统iso文件四、下载好后一步步按图示要求成功操作一、先从微软中国官网https://www.microsoft.com/zh-cn/进去 二、然后按图示一…

HackMyVM-Animetronic

目录 信息收集 arp nmap nikto whatweb WEB web信息收集 feroxbuster steghide exiftool hydra ssh连接 提权 系统信息收集 socat提权 信息收集 arp ┌──(root㉿0x00)-[~/HackMyVM] └─# arp-scan -l Interface: eth0, type: EN10MB, MAC: 08:00:27:9d:6d:7…

代码随想录第52天|300.最长递增子序列 718. 最长重复子数组

300.最长递增子序列 300. 最长递增子序列 - 力扣&#xff08;LeetCode&#xff09; 代码随想录 (programmercarl.com) 动态规划之子序列问题&#xff0c;元素不连续&#xff01;| LeetCode&#xff1a;300.最长递增子序列_哔哩哔哩_bilibili 给你一个整数数组 nums &#xff0…