[Spring] Thread Pool 설정하기 (with. ThreadPoolTaskExecutor)

enjoy89·2024년 5월 14일
0
post-custom-banner

이전 [Spring] 비동기(Asynchronous) 처리 포스팅에서 @EnableAsync와 @Async 어노테이션을 통해 간편하게 비동기 처리를 구현할 수 있다까지만 살펴보고 따로 쓰레드 풀을 어떻게 설정해야되는지에 대한 내용이 없는 것 같아서 추가로 내용을 작성하였습니다.

쓰레드 풀(Thread Pool)이란?

특정 수의 쓰레드를 미리 생성해 놓고, 필요할 때마다 이 쓰레드들을 재사용하여 작업을 처리하는 방식

서버는 동시에 여러 사용자가 접속할 수 있습니다. 이때 동시 접속자가 많아지게 되면 쓰레드가 무한대로 생성되면서 서버가 다운될 수 있습니다. 이때 쓰레드 풀을 설정하여 시스템 자원의 효율적 사용을 가능하게 합니다.

쓰레드 풀의 필요성

  1. 시스템 리소스 효율적 사용 : 스레드 생성과 제거는 시스템에 상당한 부하를 줄 수 있습니다. 스레드 풀을 사용하면 미리 생성된 스레드를 재사용할 수 있으므로 이러한 부하를 줄일 수 있습니다.
  2. 동시성 제어 : 스레드 풀은 동시에 실행할 수 있는 스레드 수를 제한함으로써 시스템의 과부하를 방지합니다. 너무 많은 작업이 동시에 실행되면 시스템이 느려질 수 있으므로, 이를 제어하는 것이 중요합니다.
  3. 응답 시간 향상 : 스레드 풀을 사용하면 새 작업을 빠르게 할당하고 처리할 수 있습니다. 이로 인해 시스템의 응답 시간이 향상될 수 있습니다.

이처럼 쓰레드 풀을 사용하면 작업 처리에 대한 오버헤드를 줄이고, 시스템의 응답 시간을 개선할 수 있습니다.


Spring에서의 쓰레드 풀

Spring의 @Async 어노테이션을 사용할 때, 별도의 TaskExecutor를 설정을 해주지 않으면, SimpleAsyncTaskExecutor가 기본적으로 사용됩니다. (이전 포스팅에서 따로 설정해주지 않았기 때문에 이 경우에 해당합니다.)

이때 SimpleAsyncTaskExecutor는 쓰레드 풀을 사용하지 않고, 매 요청마다 새로운 쓰레드를 생성해서 작업을 수행합니다. ⇒ 따라서 이는 권장되지 않는 방법입니다.

별도로 쓰레드 풀 설정을 해주어야, 시스템 과부하를 방지할 수 있습니다.

Spring에서는 ThreadPoolTaskExecutor를 통해 쓰레드 풀을 쉽게 설정할 수 있습니다.


ThreadPoolTaskExecutor 설정 방법

ThreadPoolTaskExecutor는 Spring의 Task Execution 추상화의 일부로, Java의 기본 java.util.concurrent.ThreadPoolExecutor의 기능을 그대로 사용하면서, 스프링 관리 환경에 적합한 추가 기능과 속성을 제공하는 확장된 클래스입니다.

아래와 같이 AsyncConfigurer 인터페이스의 getAsyncExecutor() 메서드를 오버라이드하여 사용할 Executor 즉, 쓰레드 풀을 정의합니다.

@Configuration
@EnableAsync
public class AsyncConfig implements AsyncConfigurer {

    @Override
    public Executor getAsyncExecutor() {
        ThreadPoolTaskExecutor executor = new ThreadPoolTaskExecutor();
        executor.setCorePoolSize(10); // 기본 스레드 수
        executor.setMaxPoolSize(20); // 최대 스레드 수
        executor.setQueueCapacity(500); // 큐 용량
        executor.setThreadNamePrefix("async-executor-");
        executor.initialize();
        return executor;
    }
}

주요 설정 옵션

corePoolSize

  • 쓰레드 풀이 유지하는 기본 쓰레드 수입니다.
  • 작업이 추가될 때 쓰레드 수가 이 값보다 작으면 새 쓰레드가 생성됩니다.
  • 너무 낮게 설정하면, 새 작업이 들어왔을 때 즉시 처리할 수 있는 쓰레드가 부족할 수 있습니다.
  • 반면, 너무 높게 설정하면, 사용하지 않는 쓰레드가 많이 생성될 수 있어 자원을 낭비할 수 있습니다.
  • 서비스의 상태를 고려하여 적절한 수준으로 설정해야 합니다.

maxPoolSize

  • 쓰레드 풀이 허용하는 최대 쓰레드 수입니다.
  • 큐가 가득 차고, 현재 실행 중인 쓰레드 수가 corePoolSize보다 많을 때까지 쓰레드 수가 증가할 수 있습니다.
  • 이 값을 너무 높게 설정하면, 너무 많은 쓰레드가 동시에 실행되어 시스템 성능이 저하될 수 있습니다.

queueCapacity

  • 작업 큐의 용량입니다.
  • 이 큐는 실행될 작업을 보관합니다. 큐가 가득 차면, 새 작업은 쓰레드가 사용 가능해질 때까지 대기합니다.
  • 이 값이 너무 작으면, 작업을 빠르게 큐에 쌓아 최대 쓰레드 수까지 쓰레드를 생성하게 될 수 있습니다. 너무 크면, 시스템 메모리를 많이 소모할 수 있습니다. 

threadNamePrefix

  • 생성되는 쓰레드의 이름 접두어입니다.
  • 디버깅할 때 유용하게 사용될 수 있습니다.

ThreadPoolExecutor의 작업 처리 과정

위에서 해준 설정을 바탕으로 ThreadPoolExecutor가 어떻게 동작하는지 그 처리 과정에 대해 알아보겠습니다.

1. 작업 수신 후 Core Pool Size 내에서 처리

  • 새로운 작업이 들어오면, ThreadPoolTaskExecutor는 먼저 설정된 corePoolSize 내에서 쓰레드를 생성합니다.
  • 이 쓰레드들을 사용하여 들어온 작업을 처리합니다.

2. Queue에 작업 저장

  • corePoolSize를 초과하는 추가 작업이 들어오면, 이 작업들은 queue에 저장됩니다.
  • 저장된 작업은 쓰레드가 사용 가능해질 때까지 대기합니다.

3. Maximum Pool Size까지 쓰레드 생성

  • queue가 가득 차면, ThreadPoolTaskExecutor는 maximumPoolSize에 도달할 때까지 추가 쓰레드를 생성합니다.
  • 이 추가 쓰레드들은 대기 중인 작업을 처리합니다.

4. Keep-Alive Time 후 쓰레드 종료

  • 작업 처리가 완료되고, 쓰레드가 설정된 keep-alive time 동안 아무 작업도 하지 않으면, 쓰레드 풀은 이 쓰레드를 종료시켜 시스템 리소스를 절약합니다.

정리

쓰레드 풀이란 특정 수의 쓰레드를 미리 생성해 놓고, 필요할 때마다 이 쓰레드들을 재사용하여 작업을 처리하는 방식입니다.

Spring에서는 ThreadPoolExecutor를 통해 쓰레드 풀을 쉽게 설정할 수 있으며, 기본 쓰레드 수(corePoolSize), 최대 쓰레드 수(maxPoolSize), 작업 큐의 용량(queueCapacity) 등을 설정하여 효율적으로 쓰레드 관리와 작업 처리를 수행하게 됩니다.

기존에는 @Async 어노테이션을 통해 간편하게 비동기 처리를 구현할 수 있다는 것만 알고 쓰레드 풀 설정을 별도로 해야된다는 것을 몰랐었는데, 쓰레드 풀의 설정 필요성과 ThreadPoolTaskExecutor 설정 방법을 알게 되었습니다.

프로젝트 서버 스펙에 맞도록 설정을 해줘야 할 것 같습니다!


Reference

https://xxeol.tistory.com/44#ThreadPoolTaskExecutor 예외 처리-1

profile
Backend Developer 💻 😺
post-custom-banner

0개의 댓글