一直没弄明白sleuth的tracerContext是如何创建和传递的,闲来无事研究了一下。由于对sleuth的源码不熟悉,准备通过debug brave.Tracer的nextId()方法,查看方法调用栈来找来龙去脉。

首先创建两个service A和B,记作srvA、srvB,在srvA中添加testA controller,sevB中添加testB controller,testA中通过Feign调用testB。

  1. 先看当用户通过浏览器调用srvA的时候,srvA是作为server的。

configuration:
TraceWebServletAutoConfiguration==>TracingFilter
TraceHttpAutoConfiguration==>HttpTracing
TraceAutoConfiguration==>Tracing
SleuthLogAutoConfiguration.Slf4jConfiguration==>CurrentTraceContext

配置中,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);}

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();}}

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。

TraceLoadBalancerFeignClient-->LoadBalancerFeignClient-->FeignLoadBalancer-->LazyTracingFeignClient-->Client

spring-cloud Sleuth相关推荐

  1. SpringCloud 2020版本教程4:使用spring cloud sleuth+zipkin实现链路追踪

    点击关注公众号,Java干货及时送达 Spring Cloud Sleuth 主要功能就是在分布式系统中提供追踪解决方案,并且兼容支持了 zipkin,你只需要在pom文件中引入相应的依赖即可. 微服 ...

  2. Spring Cloud Sleuth 进阶实战

    为什么需要Spring Cloud Sleuth 微服务架构是一个分布式架构,它按业务划分服务单元,一个分布式系统往往有很多个服务单元.由于服务单元数量众多,业务的复杂性,如果出现了错误和异常,很难去 ...

  3. 企业分布式微服务云SpringCloud SpringBoot mybatis (九)服务链路追踪(Spring Cloud Sleuth)...

    这篇文章主要讲述服务追踪组件zipkin,Spring Cloud Sleuth集成了zipkin组件. 一.简介 Add sleuth to the classpath of a Spring Bo ...

  4. Spring Cloud Sleuth 服务跟踪 将跟踪信息存储到数据库

    参见上一篇博客:Spring Cloud Sleuth 服务跟踪 参考:zipkin使用mysql保存数据 主要在跟踪服务上配置: 在数据库创建数据库表:(可不创建,在classpath中添加对应的s ...

  5. springcloud上传文件_Spring Cloud实战:服务链路追踪Spring Cloud Sleuth

    推荐阅读: Spring全家桶笔记:Spring+Spring Boot+Spring Cloud+Spring MVC 一个SpringBoot问题就干趴下了?我却凭着这份PDF文档吊打面试官. 前 ...

  6. java版b2b2c社交电商springcloud分布式微服务 (九)服务链路追踪(Spring Cloud Sleuth)...

    电子商务社交平台源码请加企鹅求求:一零三八七七四六二六.这篇文章主要讲述服务追踪组件zipkin,Spring Cloud Sleuth集成了zipkin组件. 一.简介 Spring cloud B ...

  7. (十二)java版b2b2c社交电商spring cloud分布式微服务:使用Spring Cloud Sleuth和Zipkin进行分布式链路跟踪...

    Spring Cloud Sleuth Spring cloud b2b2c电子商务社交平台源码请加企鹅求求:一零三八七七四六二六.一般的,一个分布式服务跟踪系统,主要有三部分:数据收集.数据存储和数 ...

  8. 基于docker部署的微服务架构(九): 分布式服务追踪 Spring Cloud Sleuth

    为什么80%的码农都做不了架构师?>>>    前言 微服务架构中完成一项功能经常会在多个服务之间远程调用(RPC),形成调用链.每个服务节点可能在不同的机器上甚至是不同的集群上,需 ...

  9. 史上最简单的SpringCloud教程 | 第九篇: 服务链路追踪(Spring Cloud Sleuth)

    转:https://blog.csdn.net/forezp/article/details/70162074 这篇文章主要讲述服务追踪组件zipkin,Spring Cloud Sleuth集成了z ...

  10. Spring Cloud sleuth with zipkin over RabbitMQ教程

    文章目录 Spring Cloud sleuth with zipkin over RabbitMQ demo zipkin server的搭建(基于mysql和rabbitMQ) 客户端环境的依赖 ...

最新文章

  1. Python源码学习:Python类机制分析-用户自定义类
  2. Gym 101334F Feel Good
  3. Android中通过ImageSwitcher实现相册滑动查看照片功能(附代码下载)
  4. 使用promise解决回调地狱_「每日一题」Promise 是什么?
  5. 根据java实体生成js实体_端午前福利!Java/Python实体书赠送
  6. IP通信基础学习第十周
  7. Jmeter 获取、读取token 供其他 HTTP 请求调用
  8. java 反编译 调试_eclipse 反编译 jar 中 .class 并打断点调试
  9. 西门子g120变频器接线图_西门子G120变频器通过IOP-2面板初始设置
  10. 深度学习的黑魔法防御术:恶意样本(Adversarial Example) 的防御策略综述
  11. java还原合并单元格_Java 合并/取消合并 Excel 单元格
  12. git如何查看缓存区文件内容_[暂存盘已满怎么解决]git暂存区的理解
  13. 其他技术 网易云音乐Mp3,通过网易官方搜索引擎获取mp3外链
  14. Python读取图像数据的常用方法
  15. excel 分组求和
  16. flash游戏教程集锦~~
  17. Swift:高级架构、流水线深度、内存延迟 转载
  18. 顺序栈的实现和两栈共享空间
  19. 求数组中元素的平均值
  20. 同相和反相比例运算放大电路

热门文章

  1. java pdfbox 提取pdf 标题_java – 使用pdfbox从PDF文件中提取文本
  2. dfs dtft dft fft
  3. Open browser failed!! Please check if you have installed the browser correct
  4. python圆柱体积代码_python实现Bencode解码方法
  5. html调整div上下顺序,用了float后div块之间的上下顺序不对了_html/css_WEB-ITnose
  6. python数据驱动创建账号_Django创建超级管理员账号和修改密码
  7. ie中加入html代码,IE中HTML编辑器的修改与使用
  8. html优化布局什么意思,HTML标签布局对seo的重要性(如:h1,p,alt等标签)
  9. android编译的错误日志,android编译遇到错误
  10. mysql ak uk_mysql表的查询