AsyncConfig.java 3.3 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495
  1. package com.xuekairui.user.config;
  2. import com.xuekairui.common.TraceIdGenerator;
  3. import lombok.extern.slf4j.Slf4j;
  4. import org.jetbrains.annotations.NotNull;
  5. import org.slf4j.MDC;
  6. import org.springframework.context.annotation.Bean;
  7. import org.springframework.context.annotation.Configuration;
  8. import org.springframework.core.task.TaskDecorator;
  9. import org.springframework.scheduling.concurrent.ThreadPoolTaskExecutor;
  10. import org.springframework.scheduling.concurrent.ThreadPoolTaskScheduler;
  11. import java.util.concurrent.Executor;
  12. import java.util.concurrent.ThreadPoolExecutor;
  13. /**
  14. * 异步任务与定时调度线程池配置
  15. */
  16. @Configuration
  17. @Slf4j
  18. public class AsyncConfig {
  19. /**
  20. * 审计日志专用线程池
  21. * 核心线程数2,最大线程数5,队列容量100
  22. * 拒绝策略:CallerRunsPolicy(队列满时由调用线程执行,保证日志不丢失)
  23. */
  24. @Bean("auditLogExecutor")
  25. public Executor auditLogExecutor() {
  26. ThreadPoolTaskExecutor executor = new ThreadPoolTaskExecutor();
  27. executor.setCorePoolSize(2);
  28. executor.setMaxPoolSize(5);
  29. executor.setQueueCapacity(100);
  30. executor.setThreadNamePrefix("audit-log-");
  31. executor.setRejectedExecutionHandler(new ThreadPoolExecutor.CallerRunsPolicy());
  32. executor.initialize();
  33. return executor;
  34. }
  35. /**
  36. * 定时任务调度线程池
  37. *
  38. * <p>Spring 默认 @Scheduled 使用单线程(scheduling-1),所有定时任务串行执行,
  39. * 若某个任务阻塞会拖累其他任务。配置独立线程池后:
  40. * <ul>
  41. * <li>各 @Scheduled 方法可并发执行,互不阻塞</li>
  42. * <li>线程名前缀 scheduling- 便于日志排查</li>
  43. * <li>核心线程数 2 覆盖当前 2 个定时任务,预留扩容空间</li>
  44. * </ul>
  45. *
  46. * <p>当前定时任务:
  47. * <ul>
  48. * <li>PaymentOrderService.cleanupExpiredOrders() — 每分钟清理过期订单</li>
  49. * <li>CrawlerQuotaGrantService.cleanupExpiredGrants() — 每小时清理过期配额</li>
  50. * </ul>
  51. */
  52. @Bean
  53. public ThreadPoolTaskScheduler taskScheduler() {
  54. ThreadPoolTaskScheduler scheduler = new ThreadPoolTaskScheduler();
  55. scheduler.setPoolSize(2);
  56. scheduler.setThreadNamePrefix("scheduling-");
  57. scheduler.setAwaitTerminationSeconds(60);
  58. scheduler.setWaitForTasksToCompleteOnShutdown(true);
  59. scheduler.setTaskDecorator(new MdcTraceIdTaskDecorator());
  60. scheduler.setErrorHandler(t ->
  61. log.error("定时任务执行异常", t));
  62. return scheduler;
  63. }
  64. /**
  65. * 定时任务 MDC 装饰器:为每个任务生成独立 traceId,便于无 HTTP 上下文时定位问题
  66. */
  67. public static class MdcTraceIdTaskDecorator implements TaskDecorator {
  68. private static final String TRACE_ID_KEY = "traceId";
  69. @NotNull
  70. @Override
  71. public Runnable decorate(@NotNull Runnable runnable) {
  72. return () -> {
  73. String traceId = generateTraceId();
  74. MDC.put(TRACE_ID_KEY, traceId);
  75. try {
  76. runnable.run();
  77. } finally {
  78. MDC.clear();
  79. }
  80. };
  81. }
  82. private String generateTraceId() {
  83. return TraceIdGenerator.generate();
  84. }
  85. }
  86. }