SkyWalking8源码(一)Tracing客户端

前言

本文基于SkyWalking8.6.0分析SkyWalking客户端(javaagent)的tracing部分。

  1. 理解SkyWalking中trace相关模型;
  2. trace跨进程传播原理;
  3. trace跨线程传播原理;
  4. 跨线程span开启和停止;

一、Segment和Span

1、TraceSegment

A {@link TraceSegment} means the segment, which exists in current {@link Thread}. And the distributed trace is formed by multi {@link TraceSegment}s, because the distributed trace crosses multi-processes, multi-threads.

在OpenTelemetry中Trace由n个Span构成;

在SkyWalking中Trace由m个Segment构成,每个Segment包含n个Span。

每个Segment包含一个线程中的Span。

当TraceSegment构造时,会分配全局唯一segmentId和traceId,链路中第一个Segment的traceId是链路的traceId。

GlobalIdGenerator#generate:segmentId和traceId的生成算法。

id=进程id(uuid).线程id.线程自增序列

整理了一份面试笔记包括了:Java面试、Spring、JVM、MyBatis、Redis、MySQL、并发编程、微服务、Linux、Springboot、SpringCloud、MQ、Kafka 面试专题

需要全套面试笔记的【点击此处即可】即可免费获取

其中线程自增序列(nextSeq)=时间戳+4位自增序列(0-9999) 。

即通过segmentId和traceId可以反向定位segment和trace生成时间。

TraceSegment#relatedGlobalTrace:

如果当前Segment非链路中第一个Segment,traceId会被覆盖为链路上下文中的traceId。

2、AbstractTracingSpan

AbstractTracingSpan是Span的抽象实现,包含了众多关键属性。

  1. spanId:span在segment下的唯一标识,从0开始自增;
  2. parentSpanId:父span的id,-1代表当前span是segment中的第一个span;
  3. operationName:操作名,如SpringMVC的{POST}/api/v1/x,RabbitMQ消费者的RabbitMQ/Topic/{exchange}/Queue/{routingKey}/Consumer;
  4. tags:标签,如http请求有http.method、status_code,db有db.statement,mq有mq.broker、mq.topic,部分标签支持搜索(searchableTracesTags);
  5. startTime/endTime:span的开始时间和结束时间,注意这里都是客户端时间
  6. componentId:组件id,官方支持的组件见ComponentsDefine,如11-Feign、14-SpringMVC;

AbstractTracingSpan#start:记录开始时间。

AbstractTracingSpan#finish:记录结束时间,并将span加入segment。

3、Span类型

span有三种类型:Local/Entry/Exist。

LocalSpan

一个普通的span,线程内部操作。

比如:

  1. 自定义LocalSpan,apm-toolkit-trace提供的Trace注解
  2. 跨线程的Runnable是LocalSpan,apm-toolkit-trace提供了TraceCrossThread注解
  3. SpringSchedule是LocalSpan;

StackBasedTracingSpan

Entry和Exist类型Span都继承StackBasedTracingSpan,特点是基于栈的数据结构可以多次start和finish

StackBasedTracingSpan解决一次用户代码调用经过多个skywalking插件的问题,比如一个普通springboot应用,入口流量会经过tomcat和springmvc插件,但是只需要记录一个EntrySpan。

StackBasedTracingSpan持有stackDepth代表当前栈的深度,每次start栈深度+1,每次finish栈深度-1。

StackBasedTracingSpan复写了父类AbstractTracingSpan的finish方法当且仅当span最后一次finish后,即最后一个插件执行完成后,才真正执行finish,记录span结束时间并加入segment。

EntrySpan

EntrySpan一般是流量入口,一个Segment(线程)中最多只有一个EntrySpan。

EntrySpan维护了一个currentMaxDepth,用于记录到达的最大深度。

EntrySpan#start:

首次start记录startTime每次调用start都会清空一些span的属性,如componentId(Tomcat是1,SpringMVC是14)。

EntrySpan#tag:

以tag为例,只有最后一次调用Span#start的业务才能记录tag数据

如Tomcat插件+SpringMVC插件:

  1. Tomcat:请求先经过Tomcat插件,创建EntrySpan,记录开始和结束时间;
  2. SpringMVC:在经过Tomcat之后,经过SpringMVC插件,这里不会再创建新EntrySpan,而是清空并覆盖Tomcat记录的数据(operationName、tag、componentId等);

ExitSpan

ExitSpan一般是流量出口,比如一次rpc调用、db查询。

ExistSpan#start:首次开启ExistSpan,记录开始时间。

ExitSpan#tag:只有首次开启ExistSpan的业务能记录tag、componentId等信息。

举例,如RestTemplate使用OkHttp客户端,对于用户来说只有一个get请求调用,但是实际经过了skywalking两个插件拦截:okhttp和RestTemplate。

由于先走RestTemplate插件,所以RestTemplate插件开启ExitSpan,记录开始时间、结束时间、tag、componentId等信息。

 

arduino

复制代码

RestTemplate template = new RestTemplate(); OkHttp3ClientHttpRequestFactory factory = new OkHttp3ClientHttpRequestFactory(); template.setRequestFactory(factory); template.getForEntity("http://service/api", String.class);

4、TraceSegmentRef

TraceSegment和Span可以通过TraceSegmentRef关联到上游segment的一个span。

TraceSegment会关联1个ref。

Span在rpc场景下只会关联1个ref,在批量消费场景下会关联n个ref。

通过TraceSegmentRef将不同进程线程之间的Segment/Span进行连接。

跨进程,通过ContextCarrier构造。

评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值