java里的sleuth_spring-cloud Sleuth的使用方法

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

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

1f4d354c07feb9d2d1985e9c83b1b346.png

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

configuration:

tracewebservletautoconfiguration==>tracingfilter

tracehttpautoconfiguration==>httptracing

traceautoconfiguration==>tracing

sleuthlogautoconfiguration.slf4jconfiguration==>currenttracecontext

27ef8fe991a64457c9c1a017b8501753.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 handler;

//tracecontext的数据提取器

final tracecontext.extractor 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 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 default = new threadlocal<>();

// inheritable as brave 3's threadlocalserverclientandlocalspanstate was inheritable

static final inheritablethreadlocal inheritable = new inheritablethreadlocal<>();

final threadlocal 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.

*

*

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 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

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

希望与广大网友互动??

点此进行留言吧!

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值