前言
本文將從零搭建分布式鏈路追蹤客戶端工具包的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;}@Overridepublic 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;}@NotNullpublic 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<RestTemplate> 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 {@Beanpublic FilterRegistrationBean<HoneyTracingFilter> honeyTracingFilter(Tracer tracer) {HoneyTracingFilter honeyTracingFilter = new HoneyTracingFilter(tracer);FilterRegistrationBean<HoneyTracingFilter> 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
<?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"><parent><artifactId>honey-tracing</artifactId><groupId>com.honey</groupId><version>1.0-SNAPSHOT</version></parent><modelVersion>4.0.0</modelVersion><artifactId>honey-starter-tracing</artifactId><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>org.springframework.boot</groupId><artifactId>spring-boot-configuration-processor</artifactId></dependency><dependency><groupId>io.opentracing</groupId><artifactId>opentracing-api</artifactId><scope>provided</scope></dependency><dependency><groupId>io.opentracing.contrib</groupId><artifactId>opentracing-spring-web</artifactId><scope>provided</scope></dependency><dependency><groupId>io.jaegertracing</groupId><artifactId>jaeger-client</artifactId><scope>provided</scope></dependency></dependencies><build><plugins><plugin><groupId>org.apache.maven.plugins</groupId><artifactId>maven-source-plugin</artifactId><executions><execution><id>attach-sources</id><goals><goal>jar</goal></goals></execution></executions></plugin></plugins></build></project>
還有一點要補充,父工程的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><packaging>pom</packaging><parent><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-parent</artifactId><version>2.7.6</version></parent><groupId>com.honey</groupId><artifactId>honey-tracing</artifactId><version>1.0-SNAPSHOT</version><dependencyManagement><dependencies><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></dependencies></dependencyManagement><modules><module>honey-starter-tracing</module><module>honey-tracing-example</module></modules></project>
二. 測試demo搭建
我們需要一個demo來測試我們的基礎Starter,并且隨著后續Starter支持的功能的增多,我們的demo也要相應的擴展更多的場景。
首先是demo的目錄結構,如下所示。
1. example-service-1
RestTemplateConfig簡單的向Spring容器注冊了一個RestTemplate的bean,如下所示。
@Configuration
public class RestTemplateConfig {@Beanpublic RestTemplate restTemplate() {return new RestTemplate();}}
RestTemplateController提供了發送接口,如下所示。
@RestController
public class RestTemplateController {@Autowiredprivate RestTemplate restTemplate;@GetMapping("/send")public void send(String url) {restTemplate.getForEntity(url, Void.class);}}
最后是application.yml和pom文件,如下所示。
server:port: 8080
<?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"><parent><artifactId>honey-tracing-example</artifactId><groupId>com.honey</groupId><version>1.0-SNAPSHOT</version></parent><modelVersion>4.0.0</modelVersion><artifactId>example-service-1</artifactId><dependencies><dependency><groupId>com.honey</groupId><artifactId>honey-starter-tracing</artifactId><version>1.0-SNAPSHOT</version></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>
2. example-service-2
RestTemplateController提供了接收接口,如下所示。
@RestController
public class RestTemplateController {@GetMapping("/receive")public void receive() {System.out.println();}}
最后是application.yml和pom文件,如下所示。
server:port: 8081
<?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"><parent><artifactId>honey-tracing-example</artifactId><groupId>com.honey</groupId><version>1.0-SNAPSHOT</version></parent><modelVersion>4.0.0</modelVersion><artifactId>example-service-2</artifactId><dependencies><dependency><groupId>com.honey</groupId><artifactId>honey-starter-tracing</artifactId><version>1.0-SNAPSHOT</version></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>
總結
在本文,首先實現了一個基礎的Starter包,為分布式鏈路追蹤提供了Servlet過濾器和RestTemplate攔截器,其次實現了一個demo,后續分布式鏈路追蹤Starter的功能,將用該demo完成測試。
原文:https://juejin.cn/post/7337216565097562149