j**a 线程异步的常见实现有:
new thread
executorservice
当然,还有其他的,例如:fork-join
下面会提到这些,下面主要针对这两个场景结合ddtrace和springboot练习。
1.8 1.21.0 com.datadoghq dd-trace-api $ io.opentracing opentracing-api 0.33.0 io.opentracing opentracing-mock 0.33.0 io.opentracing opentracing-util 0.33.0 ..有关如何使用 DDTtrace SDK 的文档,请参阅文档
ddtrace-api
使用说明。
配置logback
让它输出traceid
跟spanid
,将如下pattern
适用于所有人appender
中间。
实现一个简单的接口来使用logback
输出日志信息并观察日志输出。
@requestmapping("/thread") @responsebody public string threadtest()请求后,日志如下。
2023-10-23 11:33:09.983 [http-nio-8086-exec-1] info com.zy.observable.ddtrace.calcfilter - dofilter,28] springboot-server 7209831467195902001 958235974016818257 - start /threadhost localhost:8086connection keep-aliveuser-agent apache-httpclient/4.5.14 (j**a/17.0.7)accept-encoding br,deflate,gzip,x-gzip2023-10-23 11:33:10.009 [http-nio-8086-exec-1] info com.zy.observable.ddtrace.controller.indexcontroller - threadtest,277] springboot-server 7209831467195902001 2587871298938674772 - this func is threadtest.2023-10-23 11:33:10.022 [http-nio-8086-exec-1] info com.zy.observable.ddtrace.calcfilter - dofilter,34] springboot-server 7209831467195902001 958235974016818257 - 结束: 线程时间: 39日志中生成了跟踪信息
为traceid
为spanid
向接口添加新线程以创建线程。
@requestmapping("/thread") @responsebody public string threadtest())start();return "success"; }通过请求相应的 URL 来观察日志输出。
2023-10-23 11:40:00.994 [http-nio-8086-exec-1] info com.zy.observable.ddtrace.controller.indexcontroller - threadtest,277] springboot-server 319673369251953601 5380270359912403278 - this func is threadtest.2023-10-23 11:40:00.995 [thread-10] info com.zy.observable.ddtrace.controller.indexcontroller - lambda$threadtest$1,279] springboot-server - this is new thread.通过日志输出发现,
new thread
方式,并且无法输出trace
信息,即trace
它没有通过。
如果我们显示看跌期权trace
可以把信息传进来吗,就去做吧。
threadlocal当前线程唯一的局部线程变量。
为了便于使用,让我们创建一个实用程序类threadlocalutil
public static final threadlocal thread_local = new threadlocal<>(然后,将当前跨度信息存储到
threadlocal
@requestmapping("/thread") @responsebody public string threadtest()",globaltracer.get().activespan().context().totraceid())new thread(()",threadlocalutil.getvalue())start();return "success"; }通过请求相应的 URL 来观察日志输出。
2023-10-23 14:14:02.339 [http-nio-8086-exec-1] info com.zy.observable.ddtrace.controller.indexcontroller - threadtest,278] springboot-server 4492960774800816442 4097884453719637622 - this func is threadtest.2023-10-23 14:14:02.340 [http-nio-8086-exec-1] info com.zy.observable.ddtrace.controller.indexcontroller - threadtest,280] springboot-server 4492960774800816442 4097884453719637622 - current traceid:44929607748008164422023-10-23 14:14:02.341 [thread-9] info com.zy.observable.ddtrace.controller.indexcontroller - lambda$threadtest$1,283] springboot-server - this is new thread.2023-10-23 14:14:02.342 [thread-9] info com.zy.observable.ddtrace.controller.indexcontroller - lambda$threadtest$1,284] springboot-server - new thread get span:null在新线程中获取外部线程
threadlocal
,得到的值为null
通过分析threadlocal
源代码是在我们使用threadlocal
之set()
方法threadlocal
我在内部使用它thread.currentthread()
原来如此threadlocal
数据存储key
,即从新线程获取变量信息时,key
有一个变化,所以我不能接受一个值。
public class threadlocal else }public t get() return setinitialvalue();
inheritablethreadlocal
扩大threadlocal
提供从父线程到子线程的值继承: 创建子线程时,子线程将接收父线程具有值的所有可继承线程局部变量的初始值。
官方解释:
this class extends threadlocal to provide inheritance of values from parent thread to child thread: when a child thread is created, the child receives initial values for all inheritable thread-local variables for which the parent has values. normally the child's values will be identical to the parent's; however, the child's value can be made an arbitrary function of the parent's by overriding the childvalue method in this class.inheritable thread-local variables are used in preference to ordinary thread-local variables when the per-thread-attribute being maintained in the variable (e.g., user id, transaction id) must be automatically transmitted to any child threads that are created.note: during the creation of a new thread, it is possible to opt out of receiving initial values for inheritable thread-local variables.为了便于使用,让我们创建一个实用程序类
inheritablethreadlocalutil.j**a
存储跨度信息。
public static final inheritablethreadlocal thread_local = new inheritablethreadlocal<>(将
threadlocalutil
将其替换为inheritablethreadlocalutil
@requestmapping("/thread") @responsebody public string threadtest()",globaltracer.get().activespan().context().totraceid())new thread(()",inheritablethreadlocalutil.getvalue())start();return "success"; }通过请求相应的 URL 来观察日志输出。
2023-10-23 14:37:05.415 [http-nio-8086-exec-1] info com.zy.observable.ddtrace.controller.indexcontroller - threadtest,278] springboot-server 8754268856419787293 5276611939997441402 - this func is threadtest.2023-10-23 14:37:05.416 [http-nio-8086-exec-1] info com.zy.observable.ddtrace.controller.indexcontroller - threadtest,280] springboot-server 8754268856419787293 5276611939997441402 - current traceid:87542688564197872932023-10-23 14:37:05.416 [thread-9] info com.zy.observable.ddtrace.controller.indexcontroller - lambda$threadtest$1,283] springboot-server - this is new thread.2023-10-23 14:37:05.417 [thread-9] info com.zy.observable.ddtrace.controller.indexcontroller - lambda$threadtest$1,284] springboot-server - new thread get span:datadog.trace.instrumentation.opentracing32.otspan@712ad7e2通过观察上述日志信息,线程已经获取到了
span
对象地址,但日志pattern
部分不是trace
信息输出,原因是,ddtrace
右logback
之ge***cpropertymap()
跟ge***c()
方法并添加跟踪信息put
自mdc
中间。
@advice.onmethodexit(suppress = throwable.class) public static void onexit( @advice.this iloggingevent event, @advice.return(typing = assigner.typing.dynamic, readonly = false) map mdc) agentspan.context context = instrumentationcontext.get(iloggingevent.class, agentspan.context.class).get(event); // nothing to add so return early if (context == null &&agenttracer.traceconfig().islogsinjectionenabled())map correlationvalues = new hashmap<>(8); if (context != null) else } string servicename = config.get().getservicename();if (null != servicename &&servicename.isempty())string env = config.get().getenv();if (null != env &&env.isempty())string version = config.get().getversion();if (null != version &&version.isempty())mdc = null != mdc ? new unionmap<>(mdc, correlationvalues) :correlationvalues; }为了允许新创建的线程的日志也获取父线程的跟踪信息,可以通过创建它来实现
span
来实现这一目标span
需要是父线程的子线程span
完成串联。
new thread(()",inheritablethreadlocalutil.getvalue())span span = null; try ",span.context().totraceid())finally }start();通过请求相应的 URL 来观察日志输出。
2023-10-23 14:51:28.969 [http-nio-8086-exec-1] info com.zy.observable.ddtrace.controller.indexcontroller - threadtest,278] springboot-server 2303424716416355903 7690232490489894572 - this func is threadtest.2023-10-23 14:51:28.969 [http-nio-8086-exec-1] info com.zy.observable.ddtrace.controller.indexcontroller - threadtest,280] springboot-server 2303424716416355903 7690232490489894572 - current traceid:23034247164163559032023-10-23 14:51:28.970 [thread-9] info com.zy.observable.ddtrace.controller.indexcontroller - lambda$threadtest$1,283] springboot-server - this is new thread.2023-10-23 14:51:28.971 [thread-9] info com.zy.observable.ddtrace.controller.indexcontroller - lambda$threadtest$1,284] springboot-server - new thread get span:datadog.trace.instrumentation.opentracing32.otspan@c3a1aae2023-10-23 14:51:28.971 [thread-9] info com.zy.observable.ddtrace.controller.indexcontroller - lambda$threadtest$1,292] springboot-server - thread:23034247164163559032023-10-23 14:51:28.971 [thread-9] info com.zy.observable.ddtrace.controller.indexcontroller - lambda$threadtest$1,294] springboot-server 2303424716416355903 5766505477412800739 - thread:2303424716416355903为什么线程中有两个日志
pattern
没有跟踪信息输出?原因在于当前线程的内部结构span
它是在日志输出之后创建的,只需要放入span
只需创建以下内容即可。
new thread(()",inheritablethreadlocalutil.getvalue())logger.info("thread:{}",span.context().totraceid())finally }start();通过请求相应的 URL 来观察日志输出。
2023-10-23 15:01:00.490 [http-nio-8086-exec-1] info com.zy.observable.ddtrace.controller.indexcontroller - threadtest,278] springboot-server 472828375731745486 6076606716618097397 - this func is threadtest.2023-10-23 15:01:00.491 [http-nio-8086-exec-1] info com.zy.observable.ddtrace.controller.indexcontroller - threadtest,280] springboot-server 472828375731745486 6076606716618097397 - current traceid:4728283757317454862023-10-23 15:01:00.492 [thread-9] info com.zy.observable.ddtrace.controller.indexcontroller - lambda$threadtest$1,291] springboot-server 472828375731745486 9214366589561638347 - this is new thread.2023-10-23 15:01:00.492 [thread-9] info com.zy.observable.ddtrace.controller.indexcontroller - lambda$threadtest$1,292] springboot-server 472828375731745486 9214366589561638347 - new thread get span:datadog.trace.instrumentation.opentracing32.otspan@12fd40f02023-10-23 15:01:00.493 [thread-9] info com.zy.observable.ddtrace.controller.indexcontroller - lambda$threadtest$1,293] springboot-server 472828375731745486 9214366589561638347 - thread:472828375731745486创建 API 并直通
executors
创造executorservice
对象。
@requestmapping("/execthread") @responsebody public string executorservicetest())return "executorservice"; }通过请求相应的 URL 来观察日志输出。
2023-10-23 15:24:41.828 [http-nio-8086-exec-1] info com.zy.observable.ddtrace.controller.indexcontroller - executorservicetest,309] springboot-server 2170215511602500482 4370366221958823908 - this func is executorservicetest.2023-10-23 15:24:41.832 [pool-2-thread-1] info com.zy.observable.ddtrace.controller.indexcontroller - lambda$executorservicetest$2,311] springboot-server 2170215511602500482 4370366221958823908 - this is executor thread.
executorservice
线程池模式自动传递跟踪信息,该信息派生自相应组件掩埋操作的 DDTRACE 实现。
J**A 支持许多线程组件框架的链接传递,例如:forkjointask
forkjoinpool
timertask
futuretask
threadpoolexecutor
等一会。
springboot-ddtrace-server