⭐ Programming/Spring

ThreadPool TaskExecutor와 Async TaskExecutor

김진한

리팩토링하던 중 Async를 남발하고 있던 걸 발견.

아래 코드는 기존에 ThreadPoolTaskExecutor이 아닌 AsyncTaskExecutor를 사용하고 있었다.

@EnableAsync
public class ExecutorConfig {

    @Bean(name = "consumerThreadPool")
    public ThreadPoolTaskExecutor consumerThreadPool() {
        ThreadPoolTaskExecutor executor = new ThreadPoolTaskExecutor();
        executor.setCorePoolSize(2);
        executor.setMaxPoolSize(2);
        executor.setQueueCapacity(2);
        executor.setThreadNamePrefix("consumer-");
        executor.initialize();
        return executor;
    }
}

 

AsyncTaskExecutor로 생성한 " consumerThreadPool "을 사용하는 곳은 @Async가 사용된 KafkaConsumerClient.process()로, KafkaConsumerClient.process() 의 내부 로직은 아래와 같이 동기적으로 작동하는 블로킹 작업이 존재했다.

1. consumer.poll()

2. consumer.commitSync()

확인해보니 동기 방식이 필요했고, @Async와 AsyncTaskExecutor을 사용할 필요가 없어서 삭제

또한, 비동기 처리를 삭제해서 발생하는 관련 예외들은 내부에서 InterruptedException & Exception 처리 중이라 잘 방어하고 있다고 생각된다.

 

추가로 ThreadPoolTaskExecutor와 AsyncTaskExecutor의 차이를 간단하게 정리하자.

ThreadPoolTaskExecutor

  • Executor 인터페이스를 구현한 클래스로, 스레드 풀을 관리
  • 사용자가 직접 스레드 풀의 크기(코어/최대), 큐 용량 등을 설정 가능
  • Spring에서 비동기 작업을 처리할 때 자주 사용
  • 내부적으로는 ExecutorService를 사용

AsyncTaskExecutor

  • Executor 인터페이스를 확장한 인터페이스로, 비동기 작업을 처리
  • ThreadPoolTaskExecutor도 이 인터페이스를 구현하므로, 둘의 역할은 유사
  • But, 스프링의 비동기 실행을 위한 더 추상적인 인터페이스로 사용되며, 여러 구현체가 존재할 수 있음