Aktuelles Setup:
Spring Boot 3.5.4 Micrometer 1.15.2 mit Brave Bridge zum Tracing von Log4j2 mit MDC für strukturierte Protokollierung ThreadPoolTaskExecutor für asynchrone Verarbeitung
Gibt es eine Möglichkeit, meinen aktuellen Ansatz sauberer zu gestalten? sieht robust aus?
Hier ist der Code
Das mache ich gerade, es scheint zu funktionieren, sieht es korrekt aus? Sehen Sie irgendwelche Probleme? Gibt es eine sauberere Lösung?
AsyncConfig.java
Code: Select all
import io.micrometer.context.ContextSnapshot;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.scheduling.annotation.EnableAsync;
import org.springframework.scheduling.concurrent.ThreadPoolTaskExecutor;
@Configuration
@EnableAsync
public class AsyncConfig {
public static final String THREAD_POOL_NAME = "threadPoolTaskExecutor";
@Value("${thread-pools.data-poller.max-size:10}")
private int threadPoolMaxSize;
@Value("${thread-pools.data-poller.core-size:5}")
private int threadPoolCoreSize;
@Value("${thread-pools.data-poller.queue-capacity:100}")
private int threadPoolQueueSize;
@Bean(name = THREAD_POOL_NAME)
public ThreadPoolTaskExecutor getThreadPoolTaskExecutor() {
ThreadPoolTaskExecutor executor = new ThreadPoolTaskExecutor();
executor.setMaxPoolSize(threadPoolMaxSize);
executor.setCorePoolSize(threadPoolCoreSize);
executor.setQueueCapacity(threadPoolQueueSize);
// Add context propagation
executor.setTaskDecorator(runnable ->
ContextSnapshot.captureAll().wrap(runnable)
);
return executor;
}
}
Code: Select all
import io.micrometer.tracing.Span;
import io.micrometer.tracing.Tracer;
import lombok.NonNull;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.scheduling.annotation.Async;
import org.springframework.stereotype.Service;
@Slf4j
@Service
@RequiredArgsConstructor
public class DataProcessor {
@NonNull
private final Tracer tracer;
public static final String THREAD_POOL_NAME = "threadPoolTaskExecutor";
@Async(THREAD_POOL_NAME)
public void processPendingData() {
Span span = tracer.nextSpan().name("process-pending-data").start();
try (Tracer.SpanInScope ws = tracer.withSpan(span)) {
log.info("Processing pending items");
// Now shows correct traceId and unique spanId!
// Business logic here
} finally {
span.end();
}
}
@Async(THREAD_POOL_NAME)
public void processRetryData() {
Span span = tracer.nextSpan().name("process-retry-data").start();
try (Tracer.SpanInScope ws = tracer.withSpan(span)) {
log.info("Processing retry items");
// Now shows correct traceId and unique spanId!
// Retry logic here
} finally {
span.end();
}
}
}
Code: Select all
import lombok.NonNull;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.scheduling.annotation.EnableScheduling;
import org.springframework.scheduling.annotation.Scheduled;
import org.springframework.stereotype.Service;
@Slf4j
@Service
@EnabledScheduling
@RequiredArgsConstructor
public class PollingService {
@NonNull
private final DataProcessor dataProcessor;
// the trace id automatically spawns for this
@Scheduled(fixedDelay = 5000)
public void pollData() {
log.info("Starting data polling");
// Shows traceId and spanId correctly in logs
// These async calls lose trace context
dataProcessor.processPendingData();
dataProcessor.processRetryData();
}
}
Mobile version