Sleuth 将 ScheduledExecutorService 包装为 LazyTraceExecutor

Sleuth wraps ScheduledExecutorService as a LazyTraceExecutor

我在启用 Sleuth 的 Spring 启动应用程序中定义了一个 ScheduledExecutorService bean。当我从 applicationContext sleuth 获取这个 bean 时,它已经用 LazyTraceExecutorService 包装了它,这将抛出 BeanNotOfRequiredTypeException。

@SpringBootTest(classes = { Config.class })
@RunWith(SpringRunner.class)
public class SleuthTest {
    @Autowired
    private ApplicationContext applicationContext;

    @Test
    public void testScheduler() {
        // this throws the BeanNotOfRequiredTypeException
        applicationContext.getBean("executorService", ScheduledExecutorService.class);
    }
}

@Configuration
@EnableAutoConfiguration
class Config {
    @Bean
    public ScheduledExecutorService executorService() {
        // Sleuth will wrap this with LazyTraceExecutor, while I would expect a TraceableScheduledExecutorService
        return new ScheduledThreadPoolExecutor(1);
    }
}

我是不是做错了什么或者这是一个错误?

org.springframework.beans.factory.BeanNotOfRequiredTypeException: Bean named 'executorService' is expected to be of type 'java.util.concurrent.ScheduledExecutorService' but was actually of type 'org.springframework.cloud.sleuth.instrument.async.LazyTraceExecutor'

    at org.springframework.beans.factory.support.AbstractBeanFactory.doGetBean(AbstractBeanFactory.java:378)
    at org.springframework.beans.factory.support.AbstractBeanFactory.getBean(AbstractBeanFactory.java:202)
    at org.springframework.context.support.AbstractApplicationContext.getBean(AbstractApplicationContext.java:1087)
    at com.esaturnus.demotool.SleuthTest.testScheduler(SleuthTest.java:36)
    at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
    at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)
    at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
    at java.lang.reflect.Method.invoke(Method.java:498)
    at org.junit.runners.model.FrameworkMethod.runReflectiveCall(FrameworkMethod.java:50)
    at org.junit.internal.runners.model.ReflectiveCallable.run(ReflectiveCallable.java:12)
    at org.junit.runners.model.FrameworkMethod.invokeExplosively(FrameworkMethod.java:47)
    at org.junit.internal.runners.statements.InvokeMethod.evaluate(InvokeMethod.java:17)
    at org.springframework.test.context.junit4.statements.RunBeforeTestMethodCallbacks.evaluate(RunBeforeTestMethodCallbacks.java:75)
    at org.springframework.test.context.junit4.statements.RunAfterTestMethodCallbacks.evaluate(RunAfterTestMethodCallbacks.java:86)
    at org.springframework.test.context.junit4.statements.SpringRepeat.evaluate(SpringRepeat.java:84)
    at org.junit.runners.ParentRunner.runLeaf(ParentRunner.java:325)
    at org.springframework.test.context.junit4.SpringJUnit4ClassRunner.runChild(SpringJUnit4ClassRunner.java:252)
    at org.springframework.test.context.junit4.SpringJUnit4ClassRunner.runChild(SpringJUnit4ClassRunner.java:94)
    at org.junit.runners.ParentRunner.run(ParentRunner.java:290)
    at org.junit.runners.ParentRunner.schedule(ParentRunner.java:71)
    at org.junit.runners.ParentRunner.runChildren(ParentRunner.java:288)
    at org.junit.runners.ParentRunner.access[=11=]0(ParentRunner.java:58)
    at org.junit.runners.ParentRunner.evaluate(ParentRunner.java:268)
    at org.springframework.test.context.junit4.statements.RunBeforeTestClassCallbacks.evaluate(RunBeforeTestClassCallbacks.java:61)
    at org.springframework.test.context.junit4.statements.RunAfterTestClassCallbacks.evaluate(RunAfterTestClassCallbacks.java:70)
    at org.junit.runners.ParentRunner.run(ParentRunner.java:363)
    at org.springframework.test.context.junit4.SpringJUnit4ClassRunner.run(SpringJUnit4ClassRunner.java:191)
    at org.junit.runner.JUnitCore.run(JUnitCore.java:137)
    at com.intellij.junit4.JUnit4IdeaTestRunner.startRunnerWithArgs(JUnit4IdeaTestRunner.java:119)
    at com.intellij.junit4.JUnit4IdeaTestRunner.startRunnerWithArgs(JUnit4IdeaTestRunner.java:42)
    at com.intellij.rt.execution.junit.JUnitStarter.prepareStreamsAndStart(JUnitStarter.java:234)
    at com.intellij.rt.execution.junit.JUnitStarter.main(JUnitStarter.java:74)

我认为它是 的副本。您是否尝试过最新的发行版本?

如果有人因为坚持使用旧版本的 Spring Sleuth 而正在阅读本文,您可以像这样配置任务调度。 (代码应该放在实现 SchedulingConfigurer 的 @Configuration @EnableScheduling class 中)。到目前为止它对我有用。

@Override
public void configureTasks(ScheduledTaskRegistrar taskRegistrar) {
    taskRegistrar.setTaskScheduler(taskScheduler());
}

// Declare this as a bean so that Spring shuts it down.
@Bean
public LazyTraceTaskScheduler taskScheduler() {
    ThreadPoolTaskScheduler taskScheduler = new ThreadPoolTaskScheduler();
    // scheduledThreadCount is an auto-wired @Value for me, from application.properties.
    // You can just hard-code it.
    taskScheduler.setPoolSize(scheduledThreadCount);
    taskScheduler.setThreadNamePrefix("scheduled-");
    taskScheduler.setErrorHandler((throwable) -> 
        log.error("Uncaught error thrown by @Scheduled method", throwable));
    taskScheduler.shutdown();
    taskScheduler.initialize();
    return new LazyTraceTaskScheduler(beanFactory, taskScheduler);
}

private static class LazyTraceTaskScheduler implements TaskScheduler {

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

    LazyTraceTaskScheduler(BeanFactory beanFactory, ThreadPoolTaskScheduler delegate) {
        this.beanFactory = beanFactory;
        this.delegate = delegate;
    }

    @Override
    public ScheduledFuture<?> schedule(Runnable task, Trigger trigger) {
        return delegate.schedule(newSpanContinuingTraceRunnable(task), trigger);
    }

    @Override
    public ScheduledFuture<?> schedule(Runnable task, Date startTime) {
        return delegate.schedule(newSpanContinuingTraceRunnable(task), startTime);
    }

    @Override
    public ScheduledFuture<?> scheduleAtFixedRate(Runnable task, Date startTime, long period) {
        return delegate.scheduleAtFixedRate(newSpanContinuingTraceRunnable(task), startTime, period);
    }

    @Override
    public ScheduledFuture<?> scheduleAtFixedRate(Runnable task, long period) {
        return delegate.scheduleAtFixedRate(newSpanContinuingTraceRunnable(task), period);
    }

    @Override
    public ScheduledFuture<?> scheduleWithFixedDelay(Runnable task, Date startTime, long delay) {
        return delegate.scheduleWithFixedDelay(newSpanContinuingTraceRunnable(task), startTime, delay);
    }

    @Override
    public ScheduledFuture<?> scheduleWithFixedDelay(Runnable task, long delay) {
        return delegate.scheduleWithFixedDelay(newSpanContinuingTraceRunnable(task), delay);
    }

    public void shutdown() {
        delegate.shutdown();
    }

    private Runnable newSpanContinuingTraceRunnable(Runnable task) {
        if (isNull(tracer())) {
            // Due to some race conditions tracer, etc. might not be ready yet. See the code for LazyTraceExecutor.
            return task;
        }
        return new SpanContinuingTraceRunnable(tracer(), traceKeys(), spanNamer(), task);
    }

    private Tracer tracer() {
        if (isNull(tracer)) {
            try {
                tracer = beanFactory.getBean(Tracer.class);
            } catch (NoSuchBeanDefinitionException e) {
                return null;
            }
        }
        return tracer;
    }

    private TraceKeys traceKeys() {
        if (isNull(traceKeys)) {
            try {
                traceKeys = beanFactory.getBean(TraceKeys.class);
            } catch (NoSuchBeanDefinitionException e) {
                return new TraceKeys();
            }
        }
        return traceKeys;
    }

    private SpanNamer spanNamer() {
        if (isNull(spanNamer)) {
            try {
                spanNamer = beanFactory.getBean(SpanNamer.class);
            } catch (NoSuchBeanDefinitionException e) {
                return new DefaultSpanNamer();
            }
        }
        return spanNamer;
    }
}