好得很程序员自学网

<tfoot draggable='sEl'></tfoot>

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:

?

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

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:

?

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

24

25

26

27

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实例化:

?

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

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

?

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

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:

?

1

2

3

4

5

6

7

8

9

10

11

12

13

14

@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():

?

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

24

25

26

27

28

29

30

31

32

33

34

35

36

37

38

39

40

41

42

43

44

45

46

47

48

49

50

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:

?

1

2

3

4

5

6

7

8

9

10

11

//返回一个新的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

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

原文链接:https://segmentfault测试数据/a/1190000018115247

查看更多关于spring-cloud Sleuth的使用方法的详细内容...

  阅读:11次