ThreadPoolTaskExecutor란? (ThreadPoolTaskExecutor vs ThreadPoolExecutor)
ThreadPoolTaskExecutor란?
public class ThreadPoolTaskExecutor extends ExecutorConfigurationSupport
implements AsyncListenableTaskExecutor, SchedulingTaskExecutor {
private final Object poolSizeMonitor = new Object();
private int corePoolSize = 1;
private int maxPoolSize = Integer.MAX_VALUE;
private int keepAliveSeconds = 60;
private int queueCapacity = Integer.MAX_VALUE;
private boolean allowCoreThreadTimeOut = false;
private boolean prestartAllCoreThreads = false;
@Nullable
private TaskDecorator taskDecorator;
@Nullable
private ThreadPoolExecutor threadPoolExecutor;
...
}
ThreadPoolTaskExecutor는 스레드 풀을 관리하고 비동기 작업을 실행하는 데 사용되는 클래스입니다.
Spring Framwork를 사용한다면 ThreadPoolTaskExecutor를 활용할 수 있습니다.
내부 구현을 살표보면 ThreadPoolExecutor를 활용하고 있습니다.
ThreadPoolExecutor에서 활용하는 익숙한 corrPoolSize, queueCapacity 등도 보입니다.
poolSizeMonitor, allowCoreThreadTimeOut, TaskDecorator등의 처음 보는 필드들도 보이며 ExecutorConfigurationSupport클래스를 구현하고 있으며 AsyncListenableTaskExecutor, SchedulingTaskExecutor 인터테이스를 구현하고 있습니다.
PoolSizeMonitor란?
모니터링을 위한 클래스입니다. 스레드 풀과 크기와 활성 스레드 수, 대기 중인 작업 수 등과 같은 스레드 풀의 상태를 추적하고 제어하는 데 사용됩니다.
allowCoreThreadTimeOut란?
해당 필드가 true로 설정되면 일정 시간동안 작업이 없으면 코어 스레드도 종료될 수 있습니다.
prestartAllCoreThreads란?
true로 설정하면 스레드 풀을 초기화할 때 코어 스레드를 미리 시작해 둡니다.
TaskDecorator란?
@FunctionalInterface
public interface TaskDecorator {
Runnable decorate(Runnable runnable);
}
Spring 4.3부터 지원하는 core 모듈의 인터페이스입니다.
실행을 앞둔 Runnable에 데코레이터를 제공하여 래퍼처럼 수행하고 일부 모니터링/통계를 제공합니다.
ExecutorConfigurationSupport란?
public abstract class ExecutorConfigurationSupport extends CustomizableThreadFactory
implements BeanNameAware, InitializingBean, DisposableBean {
protected final Log logger = LogFactory.getLog(getClass());
private ThreadFactory threadFactory = this;
private boolean threadNamePrefixSet = false;
private RejectedExecutionHandler rejectedExecutionHandler = new ThreadPoolExecutor.AbortPolicy();
private boolean waitForTasksToCompleteOnShutdown = false;
private long awaitTerminationMillis = 0;
@Nullable
private String beanName;
@Nullable
private ExecutorService executor;
...
@Override
public void destroy() {
shutdown();
}
...
}
ExecutorService에 대한 설정을 지원해 주며, Spring Bean에서 일반적인 라이프 사이클을 처리합니다.
해당 클래스는 DisposableBean 인터페이스를 구현하고 있으며 destory() 메서드로 빈 객체 소멸 시 필요한 기능을 구현합니다.
자세히 살펴보면 destory() 시에 shutdown()을 호출합니다.
기본적으로는 waitForTasksToCompleteOnShutdown 설정에 따라 남아있는 테스크를 전부 다 취소할지 일정시간 기다렸다가 종료할지 설정할 수 있습니다.
갑자기 Spring Boot 앱이 종료되면 스레드의 작업들은 어떻게 되지?
이걸 위한 빌드업이었습니다.
클라이언트 중에서는 비동기로 이미 OK 응답을 받았을 수도 있습니다.
kill `pgrep java` 터미널 명령어로 process 종료해 보기
kill `pgrep java`
자연스럽게 실행되다가 중간에 종료됩니다.
2023-09-30T16:31:37.677+09:00 INFO 5031 --- [ test-task-1] com.example.study.log.Logging : 20초 작업 start
Process finished with exit code 143 (interrupted by signal 15: SIGTERM)
ThreadPoolTaskExecutor 실습
@Component
class MyThreadPool{
@Bean
fun myExecutor(): ThreadPoolTaskExecutor{
val executor = ThreadPoolTaskExecutor()
executor.corePoolSize = 10
executor.maxPoolSize = 10
executor.queueCapacity = 0
executor.setThreadNamePrefix("test-task-")
executor.setWaitForTasksToCompleteOnShutdown(true)
executor.setAwaitTerminationSeconds(25) // 기본 0초로 설정되어 있음. task 내려갈 때까지 25초 기다리고 앱 종료하도록 설정
executor.initialize()
return executor
}
}
@RestController
class GraceFullShutDownTestController(
private val myExecutor: ThreadPoolTaskExecutor,
) {
@GetMapping("/task")
fun runLongTimeTask(){
val myTaskProcess = {
logger.info{"20초 작업 start"}
Thread.sleep(20000)
logger.info{"20초 작업 end"}
}
myExecutor.submit(myTaskProcess)
}
}
25초에 대한 대기시간을 주고, 20초의 작업을 수행시켜 봅니다.
task를 수행시키고 명령어로 종료시키더라도 아까와 같이 바로 SIGTERM 15가 날아오지 않고 25초 뒤에 애플리케이션이 종료됩니다.
참고자료
https://sungjk.github.io/2023/05/22/spring-boot-graceful-shutdown.html
https://www.baeldung.com/spring-boot-graceful-shutdown
https://kwonnam.pe.kr/wiki/springframework/async
https://github.com/kwon37xi/java-spring-thread-pool-test