You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

配置Micrometer实现异步操作自动创建新Span

解决Spring Boot 3.2异步方法生成新Span的问题

核心问题分析

你的配置仅实现了上下文传递,但未在异步任务执行阶段触发新Span的创建;手动在getAsyncExecutor()里生成Span是在配置初始化时执行的,而非每个异步任务执行时,因此实际方法仍使用外部Span。另外@NewSpan需要Micrometer Tracing的切面支持,且异步执行器要正确绑定追踪上下文。

正确实现步骤

1. 确认依赖配置

确保引入Micrometer Tracing相关依赖(以Maven为例):

<dependency>
    <groupId>io.micrometer</groupId>
    <artifactId>micrometer-tracing</artifactId>
</dependency>
<dependency>
    <groupId>io.micrometer</groupId>
    <artifactId>micrometer-tracing-bridge-brave</artifactId>
</dependency>

2. 自定义异步执行器,在任务执行时创建新Span

不要在执行器初始化阶段创建Span,而是包装任务,在任务执行时生成新Span并绑定上下文:

@Configuration
class MicrometerConfig implements AsyncConfigurer {
    private final Tracer tracer;
    private final ContextSnapshotFactory contextSnapshotFactory = ContextSnapshotFactory.builder().build();

    // 注入Tracer实例
    public MicrometerConfig(Tracer tracer) {
        this.tracer = tracer;
    }

    @Override
    public Executor getAsyncExecutor() {
        ThreadPoolTaskExecutor executor = new ThreadPoolTaskExecutor();
        executor.setCorePoolSize(5);
        executor.setMaxPoolSize(10);
        executor.setQueueCapacity(25);
        executor.initialize();

        // 包装执行逻辑,确保每个异步任务生成独立Span
        return runnable -> {
            // 捕获当前上下文(包含父Span信息)
            ContextSnapshot snapshot = contextSnapshotFactory.captureAll();
            executor.execute(() -> {
                try (var ignored = snapshot.wrap()) {
                    // 创建新Span,关联父Span作为子Span
                    Span newSpan = tracer.nextSpan().name("async-task").start();
                    try (Tracer.SpanInScope spanInScope = tracer.withSpan(newSpan)) {
                        runnable.run();
                    } finally {
                        // 必须结束Span,避免资源泄漏
                        newSpan.end();
                    }
                }
            });
        };
    }
}

3. 正确使用@NewSpan注解

若要使用@NewSpan,需满足以下条件:

  • 异步方法所在类是Spring管理的Bean;
  • 避免在同一类中直接调用@Async方法(切面无法生效),需通过Bean代理调用;
  • 可结合@SpanTag传递自定义标签,示例:
@Service
public class AsyncService {
    @Async
    @NewSpan(name = "custom-async-operation")
    public void executeAsyncTask(@SpanTag("task-id") String taskId) {
        // 业务逻辑实现
    }
}

4. 配置日志格式验证结果

在application.yml中配置日志格式,确保TraceId和SpanId输出:

logging:
  pattern:
    level: "%5p [${spring.application.name:},%X{traceId:-},%X{spanId:-}]"

关键注意事项

  • 新Span必须在任务执行时创建,而非执行器初始化阶段;
  • 需正确捕获父上下文,确保新Span与父Span形成父子关联;
  • 任务执行完毕后必须调用newSpan.end()结束Span,防止Span泄漏。

内容的提问来源于stack exchange,提问作者Joseph K. Strauss

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.26 17:50:18