天天看點

鍊路追蹤之sleuth全生命周期分析

背景

項目DDD重構後,所有依賴重新整理,試運作期間發現鍊路追蹤的抓取資訊不夠豐富,就翻下原來的項目源碼,再看下對應的改造下。

環境依賴

  • kafka
  • elasticsearch 7.10.0 (jdk11)
  • Kibana 7.10
  • zipkin server

sleuth

随着微服務的應用,我們運維系統時面臨以下問題

  • 真實情況的請求鍊路是什麼?
  • 鍊路請求過程中每個耗時了多少?
  • 請求的參數以及每個系統的響應是什麼?

分布式鍊路追蹤解決了以上這些問題(當然需要自己手動擴充下)

分布式鍊路追蹤(Distributed Tracing),就是将一次分布式請求,通過上下文形成調用鍊路,進行日志記錄,性能監控,并将一次分布式請求的調用情況集中展示。比如各個服務節點上的耗時、請求具體到達哪台機器上、每個服務節點的請求狀态等等。

Spring Cloud Sleuth 主要功能就是在分布式系統中提供追蹤解決方案,并且相容支援了 zipkin,它大量借鑒了Google Dapper 的設計。

我們看下整體的結構

鍊路追蹤之sleuth全生命周期分析

依賴引入

<spring-boot.version>2.3.10.RELEASE</spring-boot.version>
        <spring-cloud.version>Hoxton.SR11</spring-cloud.version>
    <parent>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-parent</artifactId>
        <version>2.3.10.RELEASE</version>
        <relativePath/>
    </parent>
    <dependency>
        <groupId>org.springframework.cloud</groupId>
        <artifactId>spring-cloud-starter-sleuth</artifactId>
    </dependency>
    <!--接入zipkin-->
    <dependency>
        <groupId>org.springframework.cloud</groupId>
        <artifactId>spring-cloud-starter-zipkin</artifactId>
    </dependency>
    <!--kafka start  -->
    <dependency>
        <groupId>org.springframework.kafka</groupId>
        <artifactId>spring-kafka</artifactId>
    </dependency>
    <dependency>
        <groupId>org.apache.kafka</groupId>
        <artifactId>kafka-clients</artifactId>
    </dependency>
           
鍊路追蹤之sleuth全生命周期分析
鍊路追蹤之sleuth全生命周期分析

我們從spring-cloud-sleuth-core 和spring-cloud-sleuth-zipkin 中檢視下spring.factories檔案。

Maven: org.springframework.cloud:spring-cloud-sleuth-core:2.2.8.RELEASE
spring.factories 檔案内容
org.springframework.boot.autoconfigure.EnableAutoConfiguration=\
org.springframework.cloud.sleuth.annotation.SleuthAnnotationAutoConfiguration,\
org.springframework.cloud.sleuth.autoconfig.TraceAutoConfiguration,\
org.springframework.cloud.sleuth.propagation.SleuthTagPropagationAutoConfiguration,\
org.springframework.cloud.sleuth.instrument.web.TraceHttpAutoConfiguration,\
org.springframework.cloud.sleuth.instrument.web.TraceWebServletAutoConfiguration,\
org.springframework.cloud.sleuth.instrument.web.client.TraceWebClientAutoConfiguration,\
org.springframework.cloud.sleuth.instrument.web.client.TraceWebAsyncClientAutoConfiguration,\
org.springframework.cloud.sleuth.instrument.async.AsyncAutoConfiguration,\
org.springframework.cloud.sleuth.instrument.async.AsyncCustomAutoConfiguration,\
org.springframework.cloud.sleuth.instrument.async.AsyncDefaultAutoConfiguration,\
org.springframework.cloud.sleuth.instrument.scheduling.TraceSchedulingAutoConfiguration,\
org.springframework.cloud.sleuth.instrument.web.client.feign.TraceFeignClientAutoConfiguration,\
org.springframework.cloud.sleuth.instrument.hystrix.SleuthHystrixAutoConfiguration,\
org.springframework.cloud.sleuth.instrument.circuitbreaker.SleuthCircuitBreakerAutoConfiguration,\
org.springframework.cloud.sleuth.instrument.rxjava.RxJavaAutoConfiguration,\
org.springframework.cloud.sleuth.instrument.reactor.TraceReactorAutoConfiguration,\
org.springframework.cloud.sleuth.instrument.web.TraceWebFluxAutoConfiguration,\
org.springframework.cloud.sleuth.instrument.zuul.TraceZuulAutoConfiguration,\
org.springframework.cloud.sleuth.instrument.rpc.TraceRpcAutoConfiguration,\
org.springframework.cloud.sleuth.instrument.grpc.TraceGrpcAutoConfiguration,\
org.springframework.cloud.sleuth.instrument.messaging.SleuthKafkaStreamsConfiguration,\
org.springframework.cloud.sleuth.instrument.messaging.TraceMessagingAutoConfiguration,\
org.springframework.cloud.sleuth.instrument.messaging.TraceSpringIntegrationAutoConfiguration,\
org.springframework.cloud.sleuth.instrument.messaging.TraceSpringMessagingAutoConfiguration,\
org.springframework.cloud.sleuth.instrument.messaging.websocket.TraceWebSocketAutoConfiguration,\
org.springframework.cloud.sleuth.instrument.opentracing.OpentracingAutoConfiguration,\
org.springframework.cloud.sleuth.instrument.redis.TraceRedisAutoConfiguration,\
org.springframework.cloud.sleuth.instrument.quartz.TraceQuartzAutoConfiguration

# Environment Post Processor
org.springframework.boot.env.EnvironmentPostProcessor=\
org.springframework.cloud.sleuth.autoconfig.TraceEnvironmentPostProcessor


Maven: org.springframework.cloud:spring-cloud-sleuth-zipkin:2.2.8.RELEASE
spring.factories 檔案内容
org.springframework.boot.autoconfigure.EnableAutoConfiguration=\
org.springframework.cloud.sleuth.zipkin2.ZipkinAutoConfiguration,\
org.springframework.cloud.sleuth.zipkin2.ZipkinBackwardsCompatibilityAutoConfiguration
           

我們先看下sleuth的TraceAutoConfiguration

鍊路追蹤之sleuth全生命周期分析

再往下看instrument下面的所有類,通過名稱我們也能推測是各種場景的插樁實作。springframework.cloud.sleuth.instrument.* 随便點進去一個,都是有條件依賴的實作(這是根據你的環境,推斷出所有的對應的插樁實作,做到鍊路跟蹤)。

我們看一個TraceHttpAutoConfiguration

@Bean
  @ConditionalOnMissingBean
  // 建構的HttpTracing在brave包中
  HttpTracing httpTracing(......){
      
  }
           

HttpTracing 中具體的servlet場景(還有一個是httpclient)

鍊路追蹤之sleuth全生命周期分析
鍊路追蹤之sleuth全生命周期分析
public final class HttpServerHandler<Req, Resp> extends HttpHandler {
    
  public Span handleReceive(HttpServerRequest request) {
    Span span = nextSpan(defaultExtractor.extract(request), request);
    //可以繼續跟下去,最後的解析在sleuth包中的SleuthHttpClientParser,裡面的tag類似于SLF4J中的MDC機制
    return handleStart(request, span);
  }
public void handleSend(@Nullable Resp response, @Nullable Throwable error, Span span) {
    if (span == null) throw new NullPointerException("span == null");
    if (response == null && error == null) {
      throw new IllegalArgumentException(
        "Either the response or error parameters may be null, but not both");
    }

    if (response == null) {
      span.error(error).finish();
      return;
    }

    HttpServerResponse serverResponse;
    if (response instanceof HttpServerResponse) {
      serverResponse = (HttpServerResponse) response;
      if (serverResponse.error() == null && error != null) {
        span.error(error);
      }
    } else {
      serverResponse = new FromResponseAdapter<>(adapter, response, error);
    }
    handleFinish(serverResponse, span);
  }  
  void handleFinish(HttpResponse response, Span span) {
    try {
      parseResponse(response, span);
    } catch (Throwable t) {
      propagateIfFatal(t);
      Platform.get().log("error parsing response {0}", response, t);
    } finally {
      long finishTimestamp = response.finishTimestamp();
      if (finishTimestamp == 0L) {
        /**
         *  這裡會進行資源的釋放
         *  追到RealSpan
         *  追到pendingSpans
         *  spanHandler
         *  ZipkinSpanHandler  最後由zipkinSpanHandler進行上送
         */
        span.finish();
      } else {
        span.finish(finishTimestamp);
      }
    }
      
  }
  //具體實作最後也在sleuth包中的SleuthHttpServerParser
  void parseResponse(HttpResponse response, Span span) {
    responseParser.parse(response, span.context(), span.customizer());
  }
}
public class ZipkinSpanHandler extends SpanHandler implements Closeable {
   public boolean end(TraceContext context, MutableSpan span, Cause cause) {
        if (!this.alwaysReportSpans && !Boolean.TRUE.equals(context.sampled())) {
            return true;
        } else {
            //最終是CompositeReporter,對,就是TraceAutoConfiguration裡的
            this.spanReporter.report(span);
            return true;
        }
    }
}
           

多翻看一些sleuth的jar中的org.springframework.cloud.sleuth.instrument.中的配置類。

我們可以推斷出

  • sleuth層隻是按照一定的條件組裝各種場景的參數建構與綁定
  • 真正的實作在brave各個instrument包中

我們再看下spring-cloud-sleuth-zipkin中的配置

yaml中的配置檔案
# zipkin sender 選擇
spring: 
  zipkin:
    compression:
      enabled: true
    sender:
      type: kafka
     

關鍵的幾個配置

ZipkinAutoConfiguration
ZipkinSenderProperties
# sender實作的條件判斷,由于我們配置的sender.type=kafka隻有kafka會執行個體化
ZipkinSenderCondition extends SpringBootCondition
# kafka具體的配置
ZipkinKafkaSenderConfiguration
           

這也就和我們第一張圖遙相呼應了。

  • sleuth 通過插樁不同的場景實作鍊路資訊采集;
  • 然後通過指定類型的方式,将這些資訊發送到zipkinserver
  • zipkinserver 将對應的資訊儲存到指定的存儲引擎(我用的是es)
  • kibana再連結es(使用者在使用kibana的時候,能從es中查資料)

最後在根據自己的需求進行入參和出參的增強。

具體可以參考:

https://github.com/yxkong/springboot-gray.git

GatewayTracingFilter  針對gateway進行了增強
TracingFilter  針對mvc進行l 增強
           

最終效果

鍊路追蹤之sleuth全生命周期分析

如果覺得對你有幫助,請關注公衆号:5ycode,後續會不斷更新哦

鍊路追蹤之sleuth全生命周期分析

繼續閱讀