一、微服务对下游组件访问的链路支持
微服务对消息队列组件(Kafka、CMQ、RocketMQ)、网关组件(微服务网关、API 网关)、数据库(Redis、MySQL、PostgreSQL、MongoDB)和 httpclient (RestTemplate、Feign)的访问操作会产生跟踪日志, TSF 会对该日志进行采集、分析、统计,这些组件的调用会展现在 TSF 平台的链路追踪中。具体使用说明如下:
·Redis 链路使用说明
·MySQL 链路使用说明
·MongoDB 链路使用说明
·Kafka 链路追踪使用说明
·RocketMQ 链路追踪使用说明
注意:说明:
注意:非微服务组件的链路支持功能依赖于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@EnableTsfpublic 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) 和自定义元数据(CustomMetadata),分别用于调用链的筛选和附带业务信息。
接口定义:
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 信息。
六、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());@Autowiredprivate 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%,以此类推。