Spring生命周期,spring-cloud Sleuth

 2023-12-06 阅读 28 评论 0

摘要:一直沒弄明白sleuth的tracerContext是如何創建和傳遞的,閑來無事研究了一下。由于對sleuth的源碼不熟悉,準備通過debug brave.Tracer的nextId()方法,查看方法調用棧來找來龍去脈。 首先創建兩個service A和B,記作srvA、srvB,在srvA中添加

一直沒弄明白sleuth的tracerContext是如何創建和傳遞的,閑來無事研究了一下。由于對sleuth的源碼不熟悉,準備通過debug brave.Tracer的nextId()方法,查看方法調用棧來找來龍去脈。

首先創建兩個service A和B,記作srvA、srvB,在srvA中添加testA controller,sevB中添加testB controller,testA中通過Feign調用testB。

clipboard.png

  1. 先看當用戶通過瀏覽器調用srvA的時候,srvA是作為server的。

Spring生命周期,configuration:
TraceWebServletAutoConfiguration==>TracingFilter
TraceHttpAutoConfiguration==>HttpTracing
TraceAutoConfiguration==>Tracing
SleuthLogAutoConfiguration.Slf4jConfiguration==>CurrentTraceContext

clipboard.png

配置中,TracingFilter在實例化時需要一個HttpTracing

  public static Filter create(HttpTracing httpTracing) {return new TracingFilter(httpTracing);}//Servlet運行時類final ServletRuntime servlet = ServletRuntime.get();//Slf4jCurrentTraceContextfinal CurrentTraceContext currentTraceContext;final Tracer tracer;final HttpServerHandler<HttpServletRequest, HttpServletResponse> handler;//TraceContext的數據提取器final TraceContext.Extractor<HttpServletRequest> extractor;TracingFilter(HttpTracing httpTracing) {tracer = httpTracing.tracing().tracer();currentTraceContext = httpTracing.tracing().currentTraceContext();handler = HttpServerHandler.create(httpTracing, ADAPTER);extractor = httpTracing.tracing().propagation().extractor(GETTER);}

HttpTracing Builder模式構造時接收一個Tracing

    Tracing tracing;//客戶端span解析器HttpClientParser clientParser;String serverName;//服務端span解析器HttpServerParser serverParser;HttpSampler clientSampler, serverSampler;Builder(Tracing tracing) {if (tracing == null) throw new NullPointerException("tracing == null");final ErrorParser errorParser = tracing.errorParser();this.tracing = tracing;this.serverName = "";// override to re-use any custom error parser from the tracing componentthis.clientParser = new HttpClientParser() {@Override protected ErrorParser errorParser() {return errorParser;}};this.serverParser = new HttpServerParser() {@Override protected ErrorParser errorParser() {return errorParser;}};this.clientSampler = HttpSampler.TRACE_ID;this.serverSampler(HttpSampler.TRACE_ID);}

Spring Framework。Tracing實例化:

    @Bean@ConditionalOnMissingBean// NOTE: stable bean name as might be used outside sleuthTracing tracing(@Value("${spring.zipkin.service.name:${spring.application.name:default}}") String serviceName,Propagation.Factory factory,CurrentTraceContext currentTraceContext,Reporter<zipkin2.Span> reporter,Sampler sampler,ErrorParser errorParser,SleuthProperties sleuthProperties) {return Tracing.newBuilder().sampler(sampler).errorParser(errorParser).localServiceName(serviceName)//ExtraFieldPropagation.Factory.propagationFactory(factory).currentTraceContext(currentTraceContext).spanReporter(adjustedReporter(reporter)).traceId128Bit(sleuthProperties.isTraceId128()).supportsJoin(sleuthProperties.isSupportsJoin()).build();}

下面看TracingFilter的doFilter:

  @Overridepublic void doFilter(ServletRequest request, ServletResponse response, FilterChain chain)throws IOException, ServletException {HttpServletRequest httpRequest = (HttpServletRequest) request;HttpServletResponse httpResponse = servlet.httpResponse(response);// Prevent duplicate spans for the same requestTraceContext context = (TraceContext) request.getAttribute(TraceContext.class.getName());if (context != null) {// A forwarded request might end up on another thread, so make sure it is scopedScope scope = currentTraceContext.maybeScope(context);try {chain.doFilter(request, response);} finally {scope.close();}return;}Span span = handler.handleReceive(extractor, httpRequest);// Add attributes for explicit access to customization or span contextrequest.setAttribute(SpanCustomizer.class.getName(), span.customizer());request.setAttribute(TraceContext.class.getName(), span.context());Throwable error = null;Scope scope = currentTraceContext.newScope(span.context());try {// any downstream code can see Tracer.currentSpan() or use Tracer.currentSpanCustomizer()chain.doFilter(httpRequest, httpResponse);} catch (IOException | ServletException | RuntimeException | Error e) {error = e;throw e;} finally {scope.close();if (servlet.isAsync(httpRequest)) { // we don't have the actual response, handle laterservlet.handleAsync(handler, httpRequest, httpResponse, span);} else { // we have a synchronous response, so we can finish the spanhandler.handleSend(ADAPTER.adaptResponse(httpRequest, httpResponse), error, span);}}}

在SleuthLogAutoConfiguration中如果有slfj的包,則注入CurrentTraceContext

    @Configuration@ConditionalOnClass(MDC.class)@EnableConfigurationProperties(SleuthSlf4jProperties.class)protected static class Slf4jConfiguration {@Bean@ConditionalOnProperty(value = "spring.sleuth.log.slf4j.enabled", matchIfMissing = true)@ConditionalOnMissingBeanpublic CurrentTraceContext slf4jSpanLogger() {return Slf4jCurrentTraceContext.create();}...}

Slf4jCurrentTraceContext中,delegate就是CurrentTraceContext.Default.inheritable()

  public static final class Default extends CurrentTraceContext {static final ThreadLocal<TraceContext> DEFAULT = new ThreadLocal<>();// Inheritable as Brave 3's ThreadLocalServerClientAndLocalSpanState was inheritablestatic final InheritableThreadLocal<TraceContext> INHERITABLE = new InheritableThreadLocal<>();final ThreadLocal<TraceContext> local;//靜態方法create,local對象為ThreadLocal類型 /** Uses a non-inheritable static thread local */public static CurrentTraceContext create() {return new Default(DEFAULT);}//local對象為InheritableThreadLocal類型//官方文檔指出,inheritable方法在線程池的環境中需謹慎使用,可能會取出錯誤的TraceContext,這樣會導致Span等信息會記錄并關聯到錯誤的traceId上/*** Uses an inheritable static thread local which allows arbitrary calls to {@link* Thread#start()} to automatically inherit this context. This feature is available as it is was* the default in Brave 3, because some users couldn't control threads in their applications.** <p>This can be a problem in scenarios such as thread pool expansion, leading to data being* recorded in the wrong span, or spans with the wrong parent. If you are impacted by this,* switch to {@link #create()}.*/public static CurrentTraceContext inheritable() {return new Default(INHERITABLE);}Default(ThreadLocal<TraceContext> local) {if (local == null) throw new NullPointerException("local == null");this.local = local;}@Override public TraceContext get() {return local.get();}//替換當前TraceContext,close方法將之前的TraceContext設置回去//Scope接口繼承了Closeable接口,在try中使用會自動調用close方法,為了避免用戶忘記close方法,還提供了Runnable,Callable,Executor,ExecutorService包裝方法@Override public Scope newScope(@Nullable TraceContext currentSpan) {final TraceContext previous = local.get();local.set(currentSpan);class DefaultCurrentTraceContextScope implements Scope {@Override public void close() {local.set(previous);}}return new DefaultCurrentTraceContextScope();}}

springcloud項目實戰,Slf4jCurrentTraceContext的delegate使用的就是一個InheritableThreadLocalInheritableThreadLocal在創建子線程的時候,會將父線程的inheritableThreadLocals繼承下來。這樣就實現了TraceContext在父子線程中的傳遞。

看一下CurrentTraceContextmaybeScope

  //返回一個新的scope,如果當前scope就是傳入的scope,返回一個空scopepublic Scope maybeScope(@Nullable TraceContext currentSpan) {//獲取當前TraceContextTraceContext currentScope = get();//如果傳入的TraceContext為空,且當前TraceContext為空返回空scopeif (currentSpan == null) {if (currentScope == null) return Scope.NOOP;return newScope(null);}return currentSpan.equals(currentScope) ? Scope.NOOP : newScope(currentSpan);}

TracingFilter中HttpServerHandler解析Request:

請輸入代碼

2.srvA請求到servB時作為Client。

springcloud負載均衡、TraceLoadBalancerFeignClient-->LoadBalancerFeignClient-->FeignLoadBalancer-->LazyTracingFeignClient-->Client

版权声明:本站所有资料均为网友推荐收集整理而来,仅供学习和研究交流使用。

原文链接:https://hbdhgg.com/3/189624.html

发表评论:

本站为非赢利网站,部分文章来源或改编自互联网及其他公众平台,主要目的在于分享信息,版权归原作者所有,内容仅供读者参考,如有侵权请联系我们删除!

Copyright © 2022 匯編語言學習筆記 Inc. 保留所有权利。

底部版权信息