背景
項目DDD重構後,所有依賴重新整理,試運作期間發現鍊路追蹤的抓取資訊不夠豐富,就翻下原來的項目源碼,再看下對應的改造下。
環境依賴
- kafka
- elasticsearch 7.10.0 (jdk11)
- Kibana 7.10
- zipkin server
sleuth
随着微服務的應用,我們運維系統時面臨以下問題
- 真實情況的請求鍊路是什麼?
- 鍊路請求過程中每個耗時了多少?
- 請求的參數以及每個系統的響應是什麼?
分布式鍊路追蹤解決了以上這些問題(當然需要自己手動擴充下)
分布式鍊路追蹤(Distributed Tracing),就是将一次分布式請求,通過上下文形成調用鍊路,進行日志記錄,性能監控,并将一次分布式請求的調用情況集中展示。比如各個服務節點上的耗時、請求具體到達哪台機器上、每個服務節點的請求狀态等等。
Spring Cloud Sleuth 主要功能就是在分布式系統中提供追蹤解決方案,并且相容支援了 zipkin,它大量借鑒了Google Dapper 的設計。
我們看下整體的結構
依賴引入
<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>
我們從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
再往下看instrument下面的所有類,通過名稱我們也能推測是各種場景的插樁實作。springframework.cloud.sleuth.instrument.* 随便點進去一個,都是有條件依賴的實作(這是根據你的環境,推斷出所有的對應的插樁實作,做到鍊路跟蹤)。
我們看一個TraceHttpAutoConfiguration
@Bean
@ConditionalOnMissingBean
// 建構的HttpTracing在brave包中
HttpTracing httpTracing(......){
}
HttpTracing 中具體的servlet場景(還有一個是httpclient)
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 增強
最終效果
如果覺得對你有幫助,請關注公衆号:5ycode,後續會不斷更新哦