java 父子线程 调用链_ZipKin原理学习--Zipkin多线程及线程池中追踪一致性问题解决...

在学习Zipkin分布式追踪系统中我们了解到Trace在整个调用链是一致的,在web服务中可以通过在header设置Trace值在不同的服务中进行传递,那样在一个服务内部不同的线程,甚至是线程池中Zipkin是如何处理的,接下来我们来了解学习一下。

单个线程

在单个线程的调用过程中,我们一般都知道通过ThreadLocal来完成在整个线程执行过程中获取相同的Trace值,Zipkin也是通过定义了一个ThreadLocal local来实现处理的。

父子线程

在主线程中新建立一个子线程时使用ThreadLocal就无效了,因此Zipkin提供了如下定义方式,使用InheritableThreadLocal定义(可以参考博客Java 多线程:InheritableThreadLocal 实现原理)

static final InheritableThreadLocal INHERITABLE = new InheritableThreadLocal<>();

这样就是存在父子线程,在创建子线程的过程中会将父线程的值全部拷贝到子线程中,这样在子线程中依然可以获取到Trace值,因此如下面的代码追踪链路依然是完整的。

@RequestMapping("/start2")

public String start(HttpServletRequest request1,HttpServletResponse response1) throws InterruptedException, IOException {

Thread thread = new Thread((new Runnable() {

@Override

public void run() {

System.err.println(Thread.currentThread().hashCode());

data = restTemplate.getForObject("http://localhost:9090/foo", String.class);

}

}));

thread.start();

return data;

}

线程池

在我们新创建一个线程,然后将线程提交给线程池时,由于线程池中线程执行的原理此时原线程中的ThreadLocal和InheritableThreadLocal都是无效的,追踪Trace值因此会丢失,导致整个调用链出现断路,如下面代码。

@RequestMapping("/start2")

public String start(HttpServletRequest request1,HttpServletResponse response1) throws InterruptedException, IOException {

String data = "";

Thread thread = new Thread((new Runnable() {

@Override

public void run() {

System.err.println(Thread.currentThread().hashCode());

data = restTemplate.getForObject("http://localhost:9090/foo", String.class);

}

}));

executor.execute(thread);

Thread.sleep(10000);

return data;

}

目前Zipkin类CurrentTraceContext给出对线程及线程池的的处理方法就是实现了Runnable重新实现了run方法,这样就解决了线程池的问题,当然不只提供了创建线程的方法,还包括线程池和Callable

public Runnable wrap(Runnable task) {

//获取父线程中的Trace

final TraceContext invocationContext = get();

class CurrentTraceContextRunnable implements Runnable {

@Override public void run() {

//将父线程中的Trace复制到子线程中

try (Scope scope = maybeScope(invocationContext)) {

task.run();

}

}

}

return new CurrentTraceContextRunnable();

}

public Scope maybeScope(@Nullable TraceContext currentSpan) {

TraceContext currentScope = get();

if (currentSpan == null) {

if (currentScope == null) return Scope.NOOP;

return newScope(null);

}

return currentSpan.equals(currentScope) ? Scope.NOOP : newScope(currentSpan);

}

public Executor executor(Executor delegate) {

class CurrentTraceContextExecutor implements Executor {

@Override public void execute(Runnable task) {

delegate.execute(CurrentTraceContext.this.wrap(task));

}

}

return new CurrentTraceContextExecutor();

}

/**

* Decorates the input such that the {@link #get() current trace context} at the time a task is

* scheduled is made current when the task is executed.

*/

public ExecutorService executorService(ExecutorService delegate) {

class CurrentTraceContextExecutorService extends brave.internal.WrappingExecutorService {

@Override protected ExecutorService delegate() {

return delegate;

}

@Override protected Callable wrap(Callable task) {

return CurrentTraceContext.this.wrap(task);

}

@Override protected Runnable wrap(Runnable task) {

return CurrentTraceContext.this.wrap(task);

}

}

return new CurrentTraceContextExecutorService();

}

public Callable wrap(Callable task) {

final TraceContext invocationContext = get();

class CurrentTraceContextCallable implements Callable {

@Override public C call() throws Exception {

try (Scope scope = maybeScope(invocationContext)) {

return task.call();

}

}

}

return new CurrentTraceContextCallable();

}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值