评论

收藏

[Java] spring-cloud Sleuth的使用方法

编程语言 编程语言 发布于:2021-09-18 18:43 | 阅读数:581 | 评论:0

这篇文章主要介绍了spring-cloud Sleuth的使用方法,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友们下面随着小编来一起学习学习吧
一直没弄明白sleuth的tracercontext是如何创建和传递的,闲来无事研究了一下。由于对sleuth的源码不熟悉,准备通过debug brave.tracer的nextid()方法,查看方法调用栈来找来龙去脉。
首先创建两个service a和b,记作srva、srvb,在srva中添加testa controller,sevb中添加testb controller,testa中通过feign调用testb。
DSC0000.png

先看当用户通过浏览器调用srva的时候,srva是作为server的。
configuration:
tracewebservletautoconfiguration==>tracingfilter
tracehttpautoconfiguration==>httptracing
traceautoconfiguration==>tracing
sleuthlogautoconfiguration.slf4jconfiguration==>currenttracecontext
DSC0001.png

配置中,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
以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持CodeAE代码之家
原文链接:https://segmentfault.com/a/1190000018115247

关注下面的标签,发现更多相似文章