|
@@ -7,11 +7,31 @@ import org.springframework.scheduling.concurrent.ThreadPoolTaskExecutor;
|
|
|
import org.springframework.scheduling.concurrent.ThreadPoolTaskScheduler;
|
|
import org.springframework.scheduling.concurrent.ThreadPoolTaskScheduler;
|
|
|
|
|
|
|
|
import java.util.concurrent.RejectedExecutionHandler;
|
|
import java.util.concurrent.RejectedExecutionHandler;
|
|
|
|
|
+import java.util.concurrent.ThreadFactory;
|
|
|
import java.util.concurrent.ThreadPoolExecutor;
|
|
import java.util.concurrent.ThreadPoolExecutor;
|
|
|
|
|
+import java.util.concurrent.atomic.AtomicInteger;
|
|
|
|
|
|
|
|
@Configuration
|
|
@Configuration
|
|
|
public class AsyncConfig {
|
|
public class AsyncConfig {
|
|
|
|
|
|
|
|
|
|
+ /**
|
|
|
|
|
+ * 守护线程工厂:LLM/LangGraph 异步调用面(taskExecutor/taskScheduler)使用非守护线程时,
|
|
|
|
|
+ * 任务挂起(如 LangGraph 服务 unresponsive)会导致进程无法退出,被外部 timeout 强杀(B-135-2
|
|
|
|
|
+ * EXIT=124 同源镜像契约 langgraph.pytest.exitHang)。注入守护线程工厂后,JVM/测试进程在任务挂起时
|
|
|
|
|
+ * 仍可正常退出。
|
|
|
|
|
+ */
|
|
|
|
|
+ private static ThreadFactory daemonThreadFactory(final String namePrefix) {
|
|
|
|
|
+ final AtomicInteger counter = new AtomicInteger(0);
|
|
|
|
|
+ return new ThreadFactory() {
|
|
|
|
|
+ @Override
|
|
|
|
|
+ public Thread newThread(Runnable r) {
|
|
|
|
|
+ Thread t = new Thread(r, namePrefix + counter.incrementAndGet());
|
|
|
|
|
+ t.setDaemon(true);
|
|
|
|
|
+ return t;
|
|
|
|
|
+ }
|
|
|
|
|
+ };
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
@Bean(name = "taskExecutor")
|
|
@Bean(name = "taskExecutor")
|
|
|
public ThreadPoolTaskExecutor taskExecutor() {
|
|
public ThreadPoolTaskExecutor taskExecutor() {
|
|
|
ThreadPoolTaskExecutor executor = new ThreadPoolTaskExecutor();
|
|
ThreadPoolTaskExecutor executor = new ThreadPoolTaskExecutor();
|
|
@@ -20,6 +40,7 @@ public class AsyncConfig {
|
|
|
executor.setQueueCapacity(100);
|
|
executor.setQueueCapacity(100);
|
|
|
executor.setKeepAliveSeconds(60);
|
|
executor.setKeepAliveSeconds(60);
|
|
|
executor.setThreadNamePrefix("cfc-async-");
|
|
executor.setThreadNamePrefix("cfc-async-");
|
|
|
|
|
+ executor.setThreadFactory(daemonThreadFactory("cfc-async-"));
|
|
|
executor.setRejectedExecutionHandler(new ThreadPoolExecutor.CallerRunsPolicy());
|
|
executor.setRejectedExecutionHandler(new ThreadPoolExecutor.CallerRunsPolicy());
|
|
|
executor.initialize();
|
|
executor.initialize();
|
|
|
return executor;
|
|
return executor;
|
|
@@ -30,6 +51,7 @@ public class AsyncConfig {
|
|
|
ThreadPoolTaskScheduler scheduler = new ThreadPoolTaskScheduler();
|
|
ThreadPoolTaskScheduler scheduler = new ThreadPoolTaskScheduler();
|
|
|
scheduler.setPoolSize(4);
|
|
scheduler.setPoolSize(4);
|
|
|
scheduler.setThreadNamePrefix("cfc-scheduler-");
|
|
scheduler.setThreadNamePrefix("cfc-scheduler-");
|
|
|
|
|
+ scheduler.setThreadFactory(daemonThreadFactory("cfc-scheduler-"));
|
|
|
scheduler.initialize();
|
|
scheduler.initialize();
|
|
|
return scheduler;
|
|
return scheduler;
|
|
|
}
|
|
}
|