일 | 월 | 화 | 수 | 목 | 금 | 토 |
---|---|---|---|---|---|---|
1 | 2 | |||||
3 | 4 | 5 | 6 | 7 | 8 | 9 |
10 | 11 | 12 | 13 | 14 | 15 | 16 |
17 | 18 | 19 | 20 | 21 | 22 | 23 |
24 | 25 | 26 | 27 | 28 | 29 | 30 |
- 자바 암호화 복호화
- 식단
- 식단일기
- 신입 개발자 필수 면접 질문
- 개발자 코딩테스트
- docker
- Linux
- 프로그래머스 코딩테스트
- .dockerignore
- 셰프의찾아가는구내식당
- 신입 개발자 자바 면접
- 신입 개발자 면접
- 개발자 면접
- 신입 개발자 자바 면접 질문
- 프로그래머스
- 도커
- 직장인점심구독
- 개발자 채용연계형인턴
- 플레이팅
- 개발자 인턴
- 개발자 정규직
- 자바 stream
- 개발자 면접 질문
- 자바 암호화 알고리즘
- 코딩테스트 연습
- 자바 암호화
- Plating
- docker image
- 신입 개발자 면접 질문
- 샐러드
- Today
- Total
달리는 두딘
[Spring] ThreadPoolTaskExecutor 설정 본문
ThreadPoolTaskExecutor는 이름에서 알 수 있듯이 스레드 풀을 사용하는 Executor입니다.
상위 인터페이스를 확인해 보면 java.util.concurrent.Executor를 Spring에서 구현한 것을 확인할 수 있습니다.
이 스레드 풀을 사용할 때 설정에 몇 가지 주의점이 필요합니다.
한번 확인해보겠습니다.
스레드 설정
@Bean("simpleTaskExecutor")
public TaskExecutor taskExecutor() {
ThreadPoolTaskExecutor taskExecutor = new ThreadPoolTaskExecutor();
taskExecutor.setCorePoolSize(5); // 기본 스레드 수
taskExecutor.setMaxPoolSize(10); // 최대 스레드 수
return taskExecutor;
}
core와 max 사이즈를 설정할 수 있습니다. 여기서 주의할 점이 있습니다. 최초 core 사이즈만큼 동작하다가 더 이상 처리할 수 없을 경우 max 사이즈만큼 스레드가 증가할 것이라고 예상할 수 있지만 사실 그렇지 않습니다.
내부적으로는 Integer.MAX_VALUE 사이즈의 LinkedBlockingQueue를 생성해서 core 사이즈만큼의 스레드에서 task를 처리할 수 없을 경우 queue에서 대기하게 됩니다. queue가 꽉 차게 되면 그때 max 사이즈만큼 스레드를 생성해서 처리하게 됩니다.
Capacity
core 사이즈 보다 많은 요청이 발생할 경우 Integer.MAX_VALUE 사이즈만큼의 queue를 이용한다고 했는데 이게 너무 크다고 생각된다면 queueCapacity 사이즈를 변경할 수 있습니다.
@Bean("simpleTaskExecutor")
public TaskExecutor taskExecutor() {
ThreadPoolTaskExecutor taskExecutor = new ThreadPoolTaskExecutor();
taskExecutor.setCorePoolSize(5); // 기본 스레드 수
taskExecutor.setMaxPoolSize(10); // 최대 스레드 수
taskExecutor.setQueueCapacity(100); // Queue 사이즈
return taskExecutor;
}
위와 같이 설정한다면 최초 5개의 스레드에서 처리하다가 처리 속도가 밀릴 경우 100개 사이즈 queue에서 대기하고 그 보다 많은 요청이 발생할 경우 최대 10개 스레드까지 생성해서 처리하게 됩니다.
RejectedExecutionHandler
max 스레드까지 생성하고 queue까지 꽉 찬 상태에서 추가 요청이 오면 RejectedExecutionException예외가 발생합니다. 더 이상 처리할 수 없다는 오류인데요. 오류가 발생하는 걸 손놓고 지켜봐야만 하는 건 아닙니다. 우리에게는 몇 가지 선택권이 있습니다.
기본적으로 RejectedExecutionHandler 인터페이스를 구현한 몇가지 클래스가 제공됩니다.
- AbortPolicy
- 기본 설정
- RejectedExecutionException을 발생시킵니다.
- DiscardOldestPolicy
- 오래된 작업을 skip 합니다.
- 모든 task가 무조건 처리되어야 할 필요가 없을 경우 사용합니다.
- DiscardPolicy
- 처리하려는 작업을 skip 합니다.
- 역시 모든 task가 무조건 처리되어야 할 필요가 없을 경우 사용합니다.
- CallerRunsPolicy
- shutdown 상태가 아니라면 ThreadPoolTaskExecutor에 요청한 thread에서 직접 처리합니다.
예외와 누락 없이 최대한 처리하려면 CallerRunsPolicy로 설정하는 것이 좋을 것 같습니다.
@Bean("simpleTaskExecutor")
public TaskExecutor taskExecutor() {
ThreadPoolTaskExecutor taskExecutor = new ThreadPoolTaskExecutor();
taskExecutor.setCorePoolSize(5); // 기본 스레드 수
taskExecutor.setMaxPoolSize(10); // 최대 스레드 수
taskExecutor.setQueueCapacity(100); // Queue 사이즈
taskExecutor.setRejectedExecutionHandler(new ThreadPoolExecutor.CallerRunsPolicy());
return taskExecutor;
}
Shutdown
이렇게 별도로 정의한 스레드 풀에서 열심히 작업이 이루어지고 있을 때 애플리케이션 종료를 요청하면 어떻게 될까요? Spring Boot Actuator를 이용해서 종료를 시켜보면 호출 즉시 application이 바로 종료되는 것을 확인할 수 있습니다.
POST http://localhost:8888/actuator/shutdown
이렇게 즉시 종료되면 아직 처리되지 못한 task는 유실되게 됩니다. 유실 없이 마지막까지 다 처리하고 종료되길 원한다면 설정을 추가해야 합니다.
@Bean("simpleTaskExecutor")
public TaskExecutor taskExecutor() {
ThreadPoolTaskExecutor taskExecutor = new ThreadPoolTaskExecutor();
taskExecutor.setCorePoolSize(5); // 기본 스레드 수
taskExecutor.setMaxPoolSize(10); // 최대 스레드 수
taskExecutor.setQueueCapacity(100); // Queue 사이즈
taskExecutor.setRejectedExecutionHandler(new ThreadPoolExecutor.CallerRunsPolicy());
taskExecutor.setWaitForTasksToCompleteOnShutdown(true);
return taskExecutor;
}
waitForTasksToCompleteOnShutdown 을 true로 하게 되면 queue에 남아 있는 모든 작업이 완료될 때까지 기다리게 됩니다.
Timeout
만약 모든 작업이 처리되길 기다리기 힘든 경우라면 최대 종료 대기 시간을 설정할 수 있습니다.
@Bean("simpleTaskExecutor")
public TaskExecutor taskExecutor() {
ThreadPoolTaskExecutor taskExecutor = new ThreadPoolTaskExecutor();
taskExecutor.setCorePoolSize(5); // 기본 스레드 수
taskExecutor.setMaxPoolSize(10); // 최대 스레드 수
taskExecutor.setQueueCapacity(100); // Queue 사이즈
taskExecutor.setRejectedExecutionHandler(new ThreadPoolExecutor.CallerRunsPolicy());
taskExecutor.setWaitForTasksToCompleteOnShutdown(true);
taskExecutor.setAwaitTerminationSeconds(60); // shutdown 최대 60초 대기
return taskExecutor;
}
결론
ThreadPoolTaskExecutor 설정을 제대로 알고 사용하지 않을 경우 예상과 다른 퍼포먼스, 오류 발생과 task 유실이 발생할 수 있으니 제대로 확인하고 사용할 필요가 있습니다.
끝.
출처
https://kapentaz.github.io/spring/Spring-ThreadPoolTaskExecutor-%EC%84%A4%EC%A0%95/#
'지식노트' 카테고리의 다른 글
[DB] Lock이란? (0) | 2022.08.18 |
---|---|
[기타] CI, DI란? (0) | 2022.08.11 |
[DB] Connection Pool이란? (2) | 2022.08.08 |
[WEB] JWT(Json Web Token)란? (2) | 2022.07.15 |
[Spring] ModelMapper (2) | 2022.07.13 |