4. 分布式链路追踪客户端工具包Starter设计
前言
本文将从零搭建分布式链路追踪客户端工具包的Starter,并将在后续文章中逐步丰富支持的场景。这里首先将搭建一个最基础的Starter,能提供的功能和1. 看完这篇文章我奶奶都懂Opentracing了一文中的示例demo类似。
相关版本依赖如下。
opentracing-api版本:0.33.0
opentracing-spring-web版本:4.1.0
jaeger-client版本:1.8.1
Springboot版本:2.7.6
github地址:honey-tracing
正文
一. 基础Starter实现
在基础Starter中,主要是提供Servlet的过滤器和RestTemplate的拦截器,我们后续的复杂功能,就将在这个基础Starter上一点一点的丰富。
首先下图是Starter的工程结构。
在基础Starter中,Servlet的链路过滤器实现如下。
public class HoneyTracingFilter implements Filter { private final Tracer tracer; public HoneyTracingFilter(Tracer tracer) { this.tracer = tracer; } @Override public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain) throws IOException, ServletException { HttpServletRequest request = (HttpServletRequest) servletRequest; 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(); 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(); } } }
RestTemplate的链路拦截器实现如下。
* RestTemplate客户端的分布式链路追踪拦截器。 */ public class HoneyRestTemplateTracingInterceptor implements ClientHttpRequestInterceptor { private final Tracer tracer; public HoneyRestTemplateTracingInterceptor(Tracer tracer) { this.tracer = tracer; } @NotNull public ClientHttpResponse intercept(@NotNull HttpRequest request, @NotNull byte[] body, ClientHttpRequestExecution execution) throws IOException { Span span = tracer.buildSpan(HONEY_REST_TEMPLATE_NAME) .withTag(Tags.SPAN_KIND.getKey(), Tags.SPAN_KIND_CLIENT) .start(); 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(); } } }
打印链路日志的HoneySpanReporter目前不打印任何日志,后面再添加打印逻辑,实现如下。
public class HoneySpanReporter implements Reporter { public void report(JaegerSpan span) { } public void close() { } }
我们使用HoneyTracingProperties来读取链路相关的配置,目前仅读取一个开关enabled,如下所示。
* 分布式链路追踪配置属性类。 */ @ConfigurationProperties("honey.tracing") public class HoneyTracingProperties { private boolean enabled; public boolean isEnabled() { return enabled; } public void setEnabled(boolean enabled) { this.enabled = enabled; } }
使用到的一些常量,存放在CommonConstants中,如下所示。
public class CommonConstants { public static final double DEFAULT_SAMPLE_RATE = 1.0; public static final String HONEY_TRACER_NAME = "HoneyTracer"; public static final String HONEY_REST_TEMPLATE_NAME = "HoneyRestTemplate"; public static final String ALL_URL_PATTERN_STR = "/*"; }
再接下来就是三个自动装配类,其中HoneyRestTemplateTracingConfig负责注册HoneyRestTemplateTracingInterceptor给容器中所有的RestTemplate,HoneyTracingConfig负责注册Tracer到Spring容器,HoneyTracingFilterConfig负责注册HoneyTracingFilter,实现如下所示。
* RestTemplate分布式链路追踪配置类。 */ @ConditionalOnBean(RestTemplate.class) @Configuration @AutoConfigureAfter(HoneyTracingConfig.class) public class HoneyRestTemplateTracingConfig { public HoneyRestTemplateTracingConfig(List restTemplates, Tracer tracer) { for (RestTemplate restTemplate : restTemplates) { restTemplate.getInterceptors().add(new HoneyRestTemplateTracingInterceptor(tracer)); } } }
* 分布式链路追踪配置类。 */ @Configuration @EnableConfigurationProperties({HoneyTracingProperties.class}) @ConditionalOnProperty(prefix = "honey.tracing", name = "enabled", havingValue = "true", matchIfMissing = true) public class HoneyTracingConfig { @Bean @ConditionalOnMissingBean(Sampler.class) public Sampler sampler() { return new ProbabilisticSampler(DEFAULT_SAMPLE_RATE); } @Bean @ConditionalOnMissingBean(Reporter.class) public Reporter reporter() { return new HoneySpanReporter(); } @Bean @ConditionalOnMissingBean(Tracer.class) public Tracer tracer(Sampler sampler, Reporter reporter) { return new JaegerTracer.Builder(HONEY_TRACER_NAME) .withTraceId128Bit() .withZipkinSharedRpcSpan() .withSampler(sampler) .withReporter(reporter) .build(); } }
* Servlet过滤器配置类。 */ @Configuration @AutoConfigureAfter(HoneyTracingConfig.class) public class HoneyTracingFilterConfig { @Bean public FilterRegistrationBean honeyTracingFilter(Tracer tracer) { HoneyTracingFilter honeyTracingFilter = new HoneyTracingFilter(tracer); FilterRegistrationBean filterFilterRegistrationBean = new FilterRegistrationBean(honeyTracingFilter); filterFilterRegistrationBean.addUrlPatterns(ALL_URL_PATTERN_STR); return filterFilterRegistrationBean; } }
最后就是spring.factories和pom文件,内容如下所示。
org.springframework.boot.autoconfigure.EnableAutoConfiguration=\ com.honey.tracing.config.HoneyTracingConfig,\ com.honey.tracing.config.HoneyTracingFilterConfig,\ com.honey.tracing.config.HoneyRestTemplateTracingConfig
honey-tracing com.honey 1.0-SNAPSHOT 4.0.0 honey-starter-tracing 8 8 org.springframework.boot spring-boot-starter-web org.springframework.boot spring-boot-configuration-processor io.opentracing opentracing-api provided io.opentracing.contrib opentracing-spring-web provided io.jaegertracing jaeger-client provided org.apache.maven.plugins maven-source-plugin attach-sources jar
还有一点要补充,父工程的pom文件如下所示。
4.0.0 pom org.springframework.boot spring-boot-starter-parent 2.7.6 com.honey honey-tracing 1.0-SNAPSHOT io.opentracing opentracing-api 0.33.0 io.opentracing.contrib opentracing-spring-web 4.1.0 io.jaegertracing jaeger-client 1.8.1 honey-starter-tracing honey-tracing-example
二. 测试demo搭建
我们需要一个demo来测试我们的基础Starter,并且随着后续Starter支持的功能的增多,我们的demo也要相应的扩展更多的场景。
首先是demo的目录结构,如下所示。
1. example-service-1
RestTemplateConfig简单的向Spring容器注册了一个RestTemplate的bean,如下所示。
@Configuration public class RestTemplateConfig { @Bean public RestTemplate restTemplate() { return new RestTemplate(); } }
RestTemplateController提供了发送接口,如下所示。
@RestController public class RestTemplateController { @Autowired private RestTemplate restTemplate; @GetMapping("/send") public void send(String url) { restTemplate.getForEntity(url, Void.class); } }
最后是application.yml和pom文件,如下所示。
server: port: 8080
honey-tracing-example com.honey 1.0-SNAPSHOT 4.0.0 example-service-1 com.honey honey-starter-tracing 1.0-SNAPSHOT io.opentracing opentracing-api 0.33.0 io.opentracing.contrib opentracing-spring-web 4.1.0 io.jaegertracing jaeger-client 1.8.1 org.projectlombok lombok
2. example-service-2
RestTemplateController提供了接收接口,如下所示。
@RestController public class RestTemplateController { @GetMapping("/receive") public void receive() { System.out.println(); } }
最后是application.yml和pom文件,如下所示。
server: port: 8081
honey-tracing-example com.honey 1.0-SNAPSHOT 4.0.0 example-service-2 com.honey honey-starter-tracing 1.0-SNAPSHOT io.opentracing opentracing-api 0.33.0 io.opentracing.contrib opentracing-spring-web 4.1.0 io.jaegertracing jaeger-client 1.8.1 org.projectlombok lombok
总结
在本文,首先实现了一个基础的Starter包,为分布式链路追踪提供了Servlet过滤器和RestTemplate拦截器,其次实现了一个demo,后续分布式链路追踪Starter的功能,将用该demo完成测试。
原文:https://juejin.cn/post/7337216565097562149