Kaynağa Gözat

fix: 调度线程池改为虚拟线程模式,防止单线程阻塞导致定时任务遗漏

- 新增 SchedulingConfig 注册 ThreadPoolTaskScheduler(poolSize=2)
- 所有 @Scheduled 方法增加 @Async("AsyncExecutor"),实际工作在虚拟线程执行
- AliyunLotClient 增加 connectTimeout/readTimeout 防止网络调用无限挂起
- 日统计执行时间从 00:30 调整为 03:00

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
skyline 1 gün önce
ebeveyn
işleme
d5ef876d8a

+ 30 - 0
car-wash-admin/src/main/java/com/kym/admin/config/SchedulingConfig.java

@@ -0,0 +1,30 @@
+package com.kym.admin.config;
+
+import lombok.extern.slf4j.Slf4j;
+import org.springframework.context.annotation.Bean;
+import org.springframework.context.annotation.Configuration;
+import org.springframework.scheduling.TaskScheduler;
+import org.springframework.scheduling.concurrent.ThreadPoolTaskScheduler;
+
+/**
+ * 调度线程池配置
+ * ScheduledExecutorService 暂不支持虚拟线程,所以用 2 线程调度器负责触发,
+ * 实际工作在 @Async("AsyncExecutor") 的虚拟线程上执行,不阻塞调度线程。
+ *
+ * @author skyline
+ */
+@Configuration
+@Slf4j
+public class SchedulingConfig {
+
+    @Bean
+    public TaskScheduler taskScheduler() {
+        log.info("初始化调度线程池(虚拟线程模式)");
+        ThreadPoolTaskScheduler scheduler = new ThreadPoolTaskScheduler();
+        scheduler.setPoolSize(2);
+        scheduler.setThreadGroupName("admin-schedule");
+        scheduler.setThreadNamePrefix("sched-");
+        scheduler.initialize();
+        return scheduler;
+    }
+}

+ 2 - 0
car-wash-admin/src/main/java/com/kym/admin/jobs/DeviceHealthProbeJob.java

@@ -4,6 +4,7 @@ import com.kym.entity.WashDevice;
 import com.kym.service.DeviceHealthProbeService;
 import com.kym.service.WashDeviceService;
 import lombok.extern.slf4j.Slf4j;
+import org.springframework.scheduling.annotation.Async;
 import org.springframework.scheduling.annotation.Scheduled;
 import org.springframework.stereotype.Component;
 
@@ -31,6 +32,7 @@ public class DeviceHealthProbeJob {
     /**
      * 每3分钟扫描所有故障状态设备,主动探测连通性
      */
+    @Async("AsyncExecutor")
     @Scheduled(cron = "0 */3 * * * ?")
     public void probeFaultDevices() {
         log.debug("设备健康探测-开始");

+ 2 - 0
car-wash-admin/src/main/java/com/kym/admin/jobs/FaultNotificationJob.java

@@ -2,6 +2,7 @@ package com.kym.admin.jobs;
 
 import com.kym.service.FaultNotificationService;
 import lombok.extern.slf4j.Slf4j;
+import org.springframework.scheduling.annotation.Async;
 import org.springframework.scheduling.annotation.Scheduled;
 import org.springframework.stereotype.Component;
 
@@ -21,6 +22,7 @@ public class FaultNotificationJob {
         this.faultNotificationService = faultNotificationService;
     }
 
+    @Async("AsyncExecutor")
     @Scheduled(cron = "0 */5 * * * ?")
     public void reconcileFaultNotifications() {
         log.debug("故障通知兜底扫描开始...");

+ 2 - 0
car-wash-admin/src/main/java/com/kym/admin/jobs/SettlementJob.java

@@ -2,6 +2,7 @@ package com.kym.admin.jobs;
 
 import com.kym.service.SettlementService;
 import lombok.extern.slf4j.Slf4j;
+import org.springframework.scheduling.annotation.Async;
 import org.springframework.scheduling.annotation.Scheduled;
 import org.springframework.stereotype.Component;
 
@@ -24,6 +25,7 @@ public class SettlementJob {
     /**
      * 每月15日 00:00 执行上月结算
      */
+    @Async("AsyncExecutor")
     @Scheduled(cron = "0 0 0 15 * ?")
     public void executeMonthlySettlement() {
         log.info("月度结算定时任务-开始");

+ 2 - 0
car-wash-admin/src/main/java/com/kym/admin/jobs/StaleOrderReconciliationJob.java

@@ -10,6 +10,7 @@ import com.kym.service.WashOrderService;
 import com.kym.service.awoara.AwoaraService;
 import lombok.extern.slf4j.Slf4j;
 import org.springframework.beans.factory.annotation.Value;
+import org.springframework.scheduling.annotation.Async;
 import org.springframework.scheduling.annotation.Scheduled;
 import org.springframework.stereotype.Component;
 
@@ -50,6 +51,7 @@ public class StaleOrderReconciliationJob {
     /**
      * 每5分钟扫描一次僵死订单
      */
+    @Async("AsyncExecutor")
     @Scheduled(cron = "0 */5 * * * ?")
     public void reconcileStaleOrders() {
         log.info("僵死订单对账-开始");

+ 5 - 2
car-wash-admin/src/main/java/com/kym/admin/jobs/StatJob.java

@@ -3,6 +3,7 @@ package com.kym.admin.jobs;
 import com.kym.service.DailyStatService;
 import com.kym.service.MonthStatService;
 import lombok.extern.slf4j.Slf4j;
+import org.springframework.scheduling.annotation.Async;
 import org.springframework.scheduling.annotation.Scheduled;
 import org.springframework.stereotype.Component;
 
@@ -26,9 +27,10 @@ public class StatJob {
 
     /**
      * 统计前一天的日数据
-     * 每天凌晨00:30执行一次
+     * 每天凌晨03:00执行一次
      */
-    @Scheduled(cron = "0 30 0 * * ?")
+    @Async("AsyncExecutor")
+    @Scheduled(cron = "0 0 3 * * ?")
     public void generateDailyStat() {
         log.info("执行站点日统计定时任务-开始");
         var yesterday = LocalDate.now().minusDays(1).format(DateTimeFormatter.ISO_LOCAL_DATE);
@@ -39,6 +41,7 @@ public class StatJob {
     /**
      * 月统计,每月5日下午15:00启动,统计上月数据
      */
+    @Async("AsyncExecutor")
     @Scheduled(cron = "0 0 15 5 * ?")
     public void generateMonthStat() {
         log.info("执行站点月统计定时任务-开始");

+ 3 - 1
car-wash-service/src/main/java/com/kym/service/aliyun/lot/AliyunLotClient.java

@@ -206,7 +206,9 @@ public class AliyunLotClient {
         com.aliyun.teaopenapi.models.Config config = new com.aliyun.teaopenapi.models.Config()
                 .setRegionId("cn-shanghai")
                 .setAccessKeyId(accessKey)
-                .setAccessKeySecret(accessSecret);
+                .setAccessKeySecret(accessSecret)
+                .setConnectTimeout(5000)
+                .setReadTimeout(10000);
         rRpcClient = new com.aliyun.iot20180120.Client(config);
     }