44import org .springframework .aop .interceptor .AsyncUncaughtExceptionHandler ;
55import org .springframework .context .annotation .Bean ;
66import org .springframework .context .annotation .Configuration ;
7+ import org .springframework .retry .annotation .EnableRetry ;
78import org .springframework .scheduling .annotation .AsyncConfigurer ;
89import org .springframework .scheduling .annotation .EnableAsync ;
10+ import org .springframework .scheduling .annotation .Scheduled ;
911import org .springframework .scheduling .concurrent .ThreadPoolTaskExecutor ;
1012
1113import java .util .concurrent .Executor ;
14+ import java .util .concurrent .ThreadPoolExecutor ;
1215
1316/**
1417 * 비동기 처리 설정
1518 * @Async 메서드를 위한 ThreadPool 및 예외 처리 구성
19+ * @Retryable 메서드를 위한 재시도 설정
1620 */
1721@ Slf4j
1822@ Configuration
1923@ EnableAsync
24+ @ EnableRetry
2025public class AsyncConfig implements AsyncConfigurer {
2126
2227 /**
23- * 비동기 작업용 ThreadPool 설정
24- * - corePoolSize: 기본 스레드 수 (5개 )
25- * - maxPoolSize: 최대 스레드 수 (10개 )
26- * - queueCapacity: 대기 큐 크기 (100개 )
28+ * 비동기 작업용 ThreadPool 설정 (보수적 증가)
29+ * - corePoolSize: 기본 스레드 수 (10개 )
30+ * - maxPoolSize: 최대 스레드 수 (20개 )
31+ * - queueCapacity: 대기 큐 크기 (200개 )
2732 * - threadNamePrefix: 스레드 이름 접두사
33+ *
34+ * 처리량: 초당 200개 이벤트 즉시 처리 + 400개 큐 대기 = 총 600개 버퍼
2835 */
2936 @ Bean (name = "taskExecutor" )
3037 @ Override
3138 public Executor getAsyncExecutor () {
3239 ThreadPoolTaskExecutor executor = new ThreadPoolTaskExecutor ();
33- executor .setCorePoolSize (5 );
34- executor .setMaxPoolSize (10 );
35- executor .setQueueCapacity (100 );
36- executor .setThreadNamePrefix ("async-" );
40+ executor .setCorePoolSize (10 ); // 5 → 10
41+ executor .setMaxPoolSize (20 ); // 10 → 20
42+ executor .setQueueCapacity (200 ); // 100 → 200
43+ executor .setThreadNamePrefix ("async-event- " );
3744 executor .setWaitForTasksToCompleteOnShutdown (true );
38- executor .setAwaitTerminationSeconds (30 );
45+ executor .setAwaitTerminationSeconds (60 ); // 30 → 60
46+
47+ // 큐 초과 시 거부 정책: 호출한 스레드에서 실행
48+ executor .setRejectedExecutionHandler (new java .util .concurrent .ThreadPoolExecutor .CallerRunsPolicy ());
49+
3950 executor .initialize ();
4051 return executor ;
4152 }
@@ -58,4 +69,36 @@ public AsyncUncaughtExceptionHandler getAsyncUncaughtExceptionHandler() {
5869 public ThreadPoolTaskExecutor taskExecutor () {
5970 return (ThreadPoolTaskExecutor ) getAsyncExecutor ();
6071 }
72+
73+ /**
74+ * 스레드 풀 모니터링 (30초마다)
75+ * - active: 현재 실행 중인 스레드 수
76+ * - queue: 큐에 대기 중인 작업 수
77+ * - completed: 완료된 작업 수
78+ */
79+ @ Scheduled (fixedDelay = 30000 )
80+ public void monitorThreadPool () {
81+ ThreadPoolTaskExecutor executor = taskExecutor ();
82+ ThreadPoolExecutor pool = executor .getThreadPoolExecutor ();
83+
84+ int activeCount = pool .getActiveCount ();
85+ int queueSize = pool .getQueue ().size ();
86+ long completedTaskCount = pool .getCompletedTaskCount ();
87+ long totalTaskCount = pool .getTaskCount ();
88+
89+ log .info ("[ThreadPool Monitoring] active={}, queue={}, completed={}, total={}" ,
90+ activeCount , queueSize , completedTaskCount , totalTaskCount );
91+
92+ // 큐가 80% 이상 차면 경고
93+ if (queueSize > 160 ) { // 200의 80%
94+ log .warn ("[ThreadPool Warning] Queue is {}% full! (size: {})" ,
95+ (queueSize * 100 / 200 ), queueSize );
96+ }
97+
98+ // 활성 스레드가 최대치에 근접하면 경고
99+ if (activeCount >= 18 ) { // 20의 90%
100+ log .warn ("[ThreadPool Warning] Active threads near max! (active: {})" ,
101+ activeCount );
102+ }
103+ }
61104}
0 commit comments