调用链快速入门

最近更新时间:2023-05-29 16:41:16

我的收藏

功能说明

为了节约用户的开发成本和提升使用效率,TSF 提供了 Spring Cloud 全链路跟踪的组件。用户只需要在代码中配置组件依赖,即可直接使用 TSF 的全链路跟踪功能,无需关心日志采集、分析、存储等过程。 用户仅需要安装了对应的依赖包及添加依赖项即可,无需其他配置。

微服务对下游组件访问的链路支持

微服务对消息队列组件(Kafka、CMQ、RocketMQ)、网关组件(微服务网关、API 网关)、数据库(Redis、MySQL、postgerSQL、MongoDB)和 httpclient (RestTemplate、Feign)的访问操作会产生跟踪日志, TSF 会对该日志进行采集、分析、统计,这些组件的调用会展现在 TSF 平台的链路追踪中。具体使用说明如下:
说明
非微服务组件的链路支持功能依赖于1.14.0版本及以上版本的 SDK,详情请参见 SDK 更新日志

前提条件

在开始开发前,请确保您已经参见 下载 Maven 下载安装了 Java 和 Maven,并且配置了 TSF 私服地址。

依赖构建及注解使用

1. 向工程中添加 spring-cloud-tsf-starter 依赖,在 pom.xml 文件中添加以下代码:
<dependency>
<groupId>com.tencent.tsf</groupId>
<artifactId>spring-cloud-tsf-starter</artifactId>
<version><!-- 调整为 SDK 长期维护(LTS)版本号 --></version>
</dependency>
spring-cloud-tsf-starter 中包含了服务注册发现、服务路由、服务鉴权、服务限流、服务熔断、服务容错、服务监控、分布式配置、调用链功能。 2. 向 Application 类中添加注解 @EnableTsf
// 下面省略了无关的代码
import org.springframework.tsf.annotation.EnableTsf;
@SpringBootApplication
@EnableTsf
public class ProviderApplication {
public static void main(String[] args) {
SpringApplication.run(ProviderApplication.class, args);
}
}
注意
如果您使用的是 1.15.0-Edgware-RELEASE/1.15.0-Finchley-RELEASE 及之前的版本,使用方法参见 Spring Cloud SDK 历史版本使用方法

标签与自定义元数据

调用链支持用户在代码中设置标签(Tag) 和自定义元数据(CustomMetada),分别用于调用链的筛选和附带业务信息。
接口定义:
public class TsfContext {
/**
* 设置多个 Tag。如果有某个 Tag 之前已经被设置过,那么它的值会被覆盖。
*/
public static void putTags(Map<String, String> tagMap, TagControlFlag... flags) {}

/**
* 设置 Tag。如果该 key 之前已经被设置过,那么它的值会被覆盖。
*/
public static void putTag(String key, String value, TagControlFlag... flags) {}

/**
* 设置 CustomMetadata。
*/
public static void putCustomMetadata(Object customMetadata)
}

注入和获取 trace 信息

TSF SDK 1.23 及之后版本,支持注入和获取 traceId、spanId、tag 信息。
说明
traceId、spanId 格式不建议自定义,可能有未知错误。
TSF 不保证注入后调用链的正确性。

Edgware 版本 SDK 使用方式

private static final Logger LOG = LoggerFactory.getLogger(MethodHandles.lookup().lookupClass());
@Autowired
private Tracer tracer;

public void addSpan() throw Exception{
//获取 traceId、spanId、tag
Span oldSpan = tracer.getCurrentSpan();
String traceId = oldSpan.getTraceId();
String spanId = oldSpan.getSpanId();
Map<String, String> tags = oldSpan.tags();
LOG.info("traceId: {}, spanId: {}", traceId, spanId);
for (String key : tags.keySet()) {
LOG.info("key: {}, value: {}", key, tags.get(key));
}

//设置 tag
tracer.addTag("http.method","get");
Map<String, String> tags1 = tracer.getCurrentSpan().tags();
for (String key : tags1.keySet()) {
LOG.info("key: {}, value: {}", key, tags1.get(key));
}

//设置 traceId、spanId(必须先新建 span)
Span.SpanBuilder spanBuilder = Span.builder();
Span span = spanBuilder.name("mockName").traceId(UUID.randomUUID().toString()).spanId(UUID.randomUUID().toString()).tag("key", "value").build();

tracer.continueSpan(span);

Span newSpan = tracer.getCurrentSpan();
String newTraceId = newSpan.getTraceId();
String newSpanId = newSpan.getSpanId();

LOG.info("traceId: {}, spanId: {}", newTraceId, newSpanId);
}

Finchley、Greenwich 版本 SDK 使用方式

private static final Logger LOG = LoggerFactory.getLogger(MethodHandles.lookup().lookupClass());
@Autowired
private Tracing tracing;

public void addSpan() throw Exception{
TraceContext traceContext = tracing.tracer().currentSpan().context();

TraceContext context = traceContext.toBuilder().traceId(2035873338086630653L).spanId(-2035873338086630653L).build();

CurrentTraceContext currentTraceContext = tracing.currentTraceContext();
currentTraceContext.newScope(context);

Span spanCur = tracing.tracer().currentSpan();


long traceId1 = spanCur.context().traceId();
long spanId1 = spanCur.context().spanId();
LOG.info("traceId1:{},spanId1:{}",traceId1, spanId1);


//添加tag
spanCur.tag("qx-test1","value");
spanCur.tag("qx-test2","value");
spanCur.tag("qx-test1","value");

//获取tag
Field state1 = spanCur.getClass().getDeclaredField("state");
state1.setAccessible(true);
MutableSpan mutableSpan1 = (MutableSpan)state1.get(spanCur);
Field tags1 = mutableSpan1.getClass().getDeclaredField("tags");
tags1.setAccessible(true);
ArrayList<String> list1 = (ArrayList<String>) tags1.get(mutableSpan1);
String key1 =null, value1 =null;
for (int i = 0, length = list1.size(); i < length; i += 2) {
key1 = list1.get(i);
value1 = list1.get(i + 1);

LOG.info("tags key1: {}, value1: {}", key1, value1);
}

//获取 traceid、spanid
TraceContext traceContext1 = spanCur.context();
long traceId2 = traceContext1.traceId();
long spanId2 = traceContext1.spanId();
LOG.info("traceId1:{},spanId1:{}",traceId2, spanId2);
}

异步线程注入调用链信息(以 Finchley、Greenwich 版 SDK 为例)

private static final Logger LOG = LoggerFactory.getLogger(MethodHandles.lookup().lookupClass());

@Autowired
private Tracing tracing;

public void setTraceContext() {
LOG.info("setTraceContext start");

// 获取调用链上下文信息
Span spanCur = tracing.tracer().currentSpan();
TraceContext traceContextCur = spanCur.context();
long traceIdCur = traceContextCur.traceId(); // traceId 是 64 位二进制的 long 类型的非零随机数
long spanIdCur = traceContextCur.spanId();
String traceIdStringCur = traceContextCur.traceIdString(); // traceIdString 是 16 位十六进制的字符串,用于向下游传递
String spanIdStringCur = HexCodec.toLowerHex(spanIdCur);
LOG.info("traceId: {}, spanId: {}, traceIdString: {}, spanIdString: {}", traceIdCur, spanIdCur, traceIdStringCur, spanIdStringCur);

ExecutorService executorService = Executors.newCachedThreadPool();
executorService.execute(() -> {
// 异步线程,调用链上下文信息为空
LOG.info("new thread, trace context is empty");

// 注入调用链上下文信息
TraceContext traceContext = TraceContext.newBuilder().traceId(traceIdCur).spanId(spanIdCur).sampled(true).build();
CurrentTraceContext currentTraceContext = tracing.currentTraceContext();
currentTraceContext.newScope(traceContext);

// 注入后获取调用链上下文信息
Span spanThread = tracing.tracer().currentSpan();
TraceContext traceContextThread = spanThread.context();
long traceIdThread = traceContextThread.traceId();
long spanIdThread = traceContextThread.spanId();
String traceIdStringThread = traceContextThread.traceIdString();
String spanIdStringThread = HexCodec.toLowerHex(spanIdThread);
LOG.info("in thread, traceId: {}, spanId: {}, traceIdString: {}, spanIdString: {}", traceIdThread, spanIdThread, traceIdStringThread, spanIdStringThread);
});

executorService.shutdown();
}

调整采样率

在 application.yml 或者 bootstrap.yml 添加如下配置:
tsf:
sleuth:
samplerRate: 0.1
说明
samplerRate 默认为1,采样率为100%;samplerRate 设置为0.1时,采样率为10%,以此类推。