Sleuth 信息在線程之間的傳遞

1. 問題

上期說,Sleuth做爲微服務下的調用鏈框架,支持traceId在各類多線程狀況下的傳遞。很抱歉,這個結論是錯的,我在這裏向你們道歉html

2. 分析

得出這個結論是由於官方文檔的一句話連接java

We’re providing LazyTraceExecutor, TraceableExecutorService and TraceableScheduledExecutorService. Those implementations are creating Spans each time a new task is submitted, invoked or scheduled.git

翻譯:咱們提供LazyTraceExecutor, TraceableExecutorService和TraceableScheduledExecutorService這三個實現類,以期在一個新線程任務提交、執行或調度時建立新的Spangithub

這句話頗有迷惑性,很容易讓人誤解(好吧我認可,目前只有我),認爲Sleuth支持各類多線程。遺憾的是,若是你手動建立了一個Thread,調用下一級服務時,Sleuth並不能感知到spring

3. 證實

3.1 方法一

寫個Demo看下,是否TraceId可以正常傳過去session

3.2 方法二

咱們看下LazyTraceExecutor是怎麼作的多線程

public class LazyTraceExecutor implements java.util.concurrent.Executor {

    private static final Log log = LogFactory.getLog(MethodHandles.lookup().lookupClass());

    private Tracer tracer;
    private final BeanFactory beanFactory;
    private final Executor delegate;
    private TraceKeys traceKeys;
    private SpanNamer spanNamer;

    public LazyTraceExecutor(BeanFactory beanFactory, Executor delegate) {
        this.beanFactory = beanFactory;
        this.delegate = delegate;
    }

    @Override
    public void execute(Runnable command) {
        if (this.tracer == null) {
            try {
                this.tracer = this.beanFactory.getBean(Tracer.class);
            }
            catch (NoSuchBeanDefinitionException e) {
                this.delegate.execute(command);
                return;
            }
        }
        this.delegate.execute(new SpanContinuingTraceRunnable(this.tracer, traceKeys(), spanNamer(), command));
    }
}

使用方式框架

@Configuration
public class MyConfiguration {
    @Autowired
    BeanFactory beanFactory;
    @Bean
    public Executor executor() {
        ThreadPoolTaskExecutor executor = new ThreadPoolTaskExecutor();
        // CUSTOMIZE HERE
        executor.setCorePoolSize(7);
        executor.setMaxPoolSize(42);
        executor.setQueueCapacity(11);
        executor.setThreadNamePrefix("MyExecutor-");
        // DON'T FORGET TO INITIALIZE
        executor.initialize();
        return new LazyTraceExecutor(this.beanFactory, executor);
    }
}

因此它能在線程之間傳遞traceId並不稀奇,由於它根本就是要你使用它的多線程工具。ide

4.可否讓子線程獲取父線程信息呢

4.1 能:InheritableThreadLocal

通常來講,每一個線程一個副本,咱們都是用ThreaLocal。但是,若是你想要該線程和它的子線程都能讀這個副本,那就能夠用InheritableThreadLocal了。
用法很簡單Demo微服務

private static final ThreadLocal<String> sessionInfoHolder1 = new ThreadLocal<String>();
    private static final ThreadLocal<String> sessionInfoHolder2 = new InheritableThreadLocal<String>();

5. 思考

既然Sleuth不支持用戶本身建立線程,而使用InheritableThreadLocal能夠解決這個問題,那麼是否是說,Sleuth這個短板實際上是能夠解決的?

5.1 方案

  1. Filter中獲取TraceId,存入:InheritableThreadLocal
  2. TraceFeignClient中判斷當前線程或其父線程中是否有traceId,若是有,就證實是用戶new的線程,便可複用Trace信息。
  3. 有興趣的讀者能夠去Sleuth拉個分支試下,說不定會被管理員merge
相關文章
相關標籤/搜索