package com.xuekairui.user.config; import com.xuekairui.common.TraceIdGenerator; import lombok.extern.slf4j.Slf4j; import org.jetbrains.annotations.NotNull; import org.slf4j.MDC; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; import org.springframework.core.task.TaskDecorator; import org.springframework.scheduling.concurrent.ThreadPoolTaskExecutor; import org.springframework.scheduling.concurrent.ThreadPoolTaskScheduler; import java.util.concurrent.Executor; import java.util.concurrent.ThreadPoolExecutor; /** * 异步任务与定时调度线程池配置 */ @Configuration @Slf4j public class AsyncConfig { /** * 审计日志专用线程池 * 核心线程数2,最大线程数5,队列容量100 * 拒绝策略:CallerRunsPolicy(队列满时由调用线程执行,保证日志不丢失) */ @Bean("auditLogExecutor") public Executor auditLogExecutor() { ThreadPoolTaskExecutor executor = new ThreadPoolTaskExecutor(); executor.setCorePoolSize(2); executor.setMaxPoolSize(5); executor.setQueueCapacity(100); executor.setThreadNamePrefix("audit-log-"); executor.setRejectedExecutionHandler(new ThreadPoolExecutor.CallerRunsPolicy()); executor.initialize(); return executor; } /** * 定时任务调度线程池 * *

Spring 默认 @Scheduled 使用单线程(scheduling-1),所有定时任务串行执行, * 若某个任务阻塞会拖累其他任务。配置独立线程池后: *

* *

当前定时任务: *

*/ @Bean public ThreadPoolTaskScheduler taskScheduler() { ThreadPoolTaskScheduler scheduler = new ThreadPoolTaskScheduler(); scheduler.setPoolSize(2); scheduler.setThreadNamePrefix("scheduling-"); scheduler.setAwaitTerminationSeconds(60); scheduler.setWaitForTasksToCompleteOnShutdown(true); scheduler.setTaskDecorator(new MdcTraceIdTaskDecorator()); scheduler.setErrorHandler(t -> log.error("定时任务执行异常", t)); return scheduler; } /** * 定时任务 MDC 装饰器:为每个任务生成独立 traceId,便于无 HTTP 上下文时定位问题 */ public static class MdcTraceIdTaskDecorator implements TaskDecorator { private static final String TRACE_ID_KEY = "traceId"; @NotNull @Override public Runnable decorate(@NotNull Runnable runnable) { return () -> { String traceId = generateTraceId(); MDC.put(TRACE_ID_KEY, traceId); try { runnable.run(); } finally { MDC.clear(); } }; } private String generateTraceId() { return TraceIdGenerator.generate(); } } }