스프링 부트로 서버를 개발하다 보면 자연스럽게 멀티스레드 환경에서 작업하게 됩니다. 스프링 부트 서버의 Thread Pool에 대해 알아보았습니다.
먼저 전체적인 구조를 이해하는 것이 중요합니다:
운영체제 (OS)
└── JVM (Java Virtual Machine)
└── Spring Boot 애플리케이션
└── 내장 Tomcat (WAS)
└── Tomcat Thread Pool
└── (extends) Java ThreadPoolExecutor
Thread 관점에서 보면:
Thread Pool은 크게 세 부분으로 구성됩니다:
Core Threads (코어 스레드)
Queue (작업 큐)
Maximum Threads (최대 스레드)
application.yml 파일에서 다음과 같이 설정할 수 있습니다:
server:
tomcat:
threads:
max: 200 # 최대 스레드 수
min-spare: 10 # 최소 스레드 수 (코어 스레드)
accept-count: 100 # 작업 큐 사이즈
max-connections: 8192 # 동시에 처리할 수 있는 최대 연결 수
connection-timeout: 20000 # 연결 타임아웃 (ms)
각 설정값은 서버의 특성과 하드웨어 사양을 고려하여 적절히 조정해야 합니다.
실제 동작 방식을 순서대로 살펴보겠습니다:
Thread Pool이 포화상태가 되었을 때의 동작을 살펴보겠습니다:
현재 상태:
- 최대 스레드(200개) 모두 사용 중
- 대기 큐(accept-count=100)도 가득 참
= 새로운 요청에 대해 connection refused 발생
@GetMapping("/api")
public String handleRequest() {
// 이미 실행 중인 이 코드는
// 서버가 새 요청을 거절하더라도
// 정상적으로 끝까지 실행됨
heavyTask();
return "done";
}
이것이 Thread Pool의 중요한 장점입니다:
Tomcat Thread Pool이 요청을 처리하는 동안 시간이 오래 걸리는 작업이 있다면, 별도의 Thread Pool을 만들어 위임하는 것이 좋습니다.
@RestController
public class MyController {
private final ExecutorService executorService = Executors.newFixedThreadPool(5);
@GetMapping("/api")
public String handleRequest() {
// Tomcat Thread가 요청을 받음
executorService.submit(() -> {
// 별도의 Thread Pool에서 무거운 작업 처리
heavyTask();
});
return "processing"; // Tomcat Thread는 빠르게 반환
}
}
ExecutorService executor = Executors.newFixedThreadPool(5);
ExecutorService executor = Executors.newCachedThreadPool();
ScheduledExecutorService executor = Executors.newScheduledThreadPool(5);
이렇게 Spring Boot의 기본 Thread Pool과 추가로 사용할 수 있는 Java Thread Pool을 적절히 활용하면, 효율적이고 안정적인 멀티스레딩 애플리케이션을 구현할 수 있습니다.
이상으로 스프링 부트 서버의 Thread Pool에 대해 알아보았습니다.