spring-cloud Sleuth的使用方法
一直没弄明白sleuth的tracercontext是如何创建和传递的,闲来无事研究了一下。由于对sleuth的源码不熟悉,准备通过debug brave.tracer的nextid()方法,查看方法调用栈来找来龙去脉。
首先创建两个service a和b,记作srva、srvb,在srva中添加testa controller,sevb中添加testb controller,testa中通过feign调用testb。
先看当用户通过浏览器调用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(); //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
以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持。