欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页  >  IT编程

spring-cloud Sleuth的使用方法

程序员文章站 2024-02-20 22:13:17
一直没弄明白sleuth的tracercontext是如何创建和传递的,闲来无事研究了一下。由于对sleuth的源码不熟悉,准备通过debug brave.tracer的n...

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

首先创建两个service a和b,记作srva、srvb,在srva中添加testa controller,sevb中添加testb controller,testa中通过feign调用testb。

spring-cloud Sleuth的使用方法

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

configuration:
tracewebservletautoconfiguration==>tracingfilter
tracehttpautoconfiguration==>httptracing
traceautoconfiguration==>tracing
sleuthlogautoconfiguration.slf4jconfiguration==>currenttracecontext

spring-cloud Sleuth的使用方法

配置中,tracingfilter在实例化时需要一个httptracing:

 public static filter create(httptracing httptracing) {
  return new tracingfilter(httptracing);
 }

 //servlet运行时类
 final servletruntime servlet = servletruntime.get();
 //slf4jcurrenttracecontext
 final 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 component
   this.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 sleuth
  tracing 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:

  span span = handler.handlereceive(extractor, httprequest);

  // add attributes for explicit access to customization or span context
  request.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 later
    servlet.handleasync(handler, httprequest, httpresponse, span);
   } else { // we have a synchronous response, so we can finish the span
    handler.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)
    @conditionalonmissingbean
    public 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 inheritable
  static 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使用的就是一个inheritablethreadlocal,inheritablethreadlocal在创建子线程的时候,会将父线程的inheritablethreadlocals继承下来。这样就实现了tracecontext在父子线程中的传递。

看一下currenttracecontext的maybescope:

 //返回一个新的scope,如果当前scope就是传入的scope,返回一个空scope
 public scope maybescope(@nullable tracecontext currentspan) {
  //获取当前tracecontext
  tracecontext currentscope = get();
  //如果传入的tracecontext为空,且当前tracecontext为空返回空scope
  if (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

 以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持。