Spring Boot 서버의 Thread Pool 이해하기

코-드 텐카이·2025년 1월 2일

Spring Boot

목록 보기
2/10

스프링 부트로 서버를 개발하다 보면 자연스럽게 멀티스레드 환경에서 작업하게 됩니다. 스프링 부트 서버의 Thread Pool에 대해 알아보았습니다.

Spring Boot 서버는 어떤 구조로 동작하나요?

먼저 전체적인 구조를 이해하는 것이 중요합니다:

운영체제 (OS)
└── JVM (Java Virtual Machine)
    └── Spring Boot 애플리케이션
        └── 내장 Tomcat (WAS)
            └── Tomcat Thread Pool
                └── (extends) Java ThreadPoolExecutor
  1. OS: 실제 물리적인 자원(CPU, 메모리 등)을 관리
  2. JVM: Java 애플리케이션을 위한 실행 환경 제공
  3. Tomcat: Java로 작성된 WAS(Web Application Server)
  4. Thread Pool: Tomcat이 Java의 ThreadPoolExecutor를 확장해서 구현

Thread 관점에서 보면:

  • JVM의 스레드는 OS의 네이티브 스레드와 1:1로 매핑됩니다
  • Tomcat의 Thread Pool도 결국 JVM 위에서 동작하는 Java 스레드들입니다
  • 따라서 Tomcat Thread Pool도 OS의 자원을 직접 사용하게 됩니다

Thread Pool은 어떤 요소들로 구성되어 있나요?

Thread Pool은 크게 세 부분으로 구성됩니다:

  1. Core Threads (코어 스레드)

    • 항상 유지되는 기본 스레드
    • min-spare로 설정 (기본값 10개)
    • 즉시 처리할 수 있는 요청의 수
  2. Queue (작업 큐)

    • 코어 스레드가 모두 사용 중일 때 요청을 저장
    • accept-count로 설정 (기본값 100)
    • TaskQueue라는 Tomcat 전용 큐 사용 (LinkedBlockingQueue 확장)
    • 처리 대기중인 요청들을 보관
  3. Maximum Threads (최대 스레드)

    • 큐가 가득 찼을 때 추가로 생성되는 스레드
    • max로 설정 (기본값 200개)
    • 급격한 부하 증가시 대응용

Tomcat Thread Pool은 어떻게 설정할 수 있나요?

application.yml 파일에서 다음과 같이 설정할 수 있습니다:

server:
  tomcat:
    threads:
      max: 200           # 최대 스레드 수
      min-spare: 10      # 최소 스레드 수 (코어 스레드)
    accept-count: 100    # 작업 큐 사이즈
    max-connections: 8192 # 동시에 처리할 수 있는 최대 연결 수
    connection-timeout: 20000  # 연결 타임아웃 (ms)

각 설정값은 서버의 특성과 하드웨어 사양을 고려하여 적절히 조정해야 합니다.

Thread Pool은 어떻게 동작하나요?

실제 동작 방식을 순서대로 살펴보겠습니다:

  1. 요청이 들어오면 먼저 코어 스레드(최소 스레드)를 활용
  2. 코어 스레드가 모두 사용중이면 큐에 작업을 저장
  3. 큐가 가득 차면 최대 스레드 수까지 추가 스레드 생성
  4. 그래도 처리가 불가능하면 요청을 거절

Thread Pool이 요청을 거절하면 어떻게 될까요?

Thread Pool이 포화상태가 되었을 때의 동작을 살펴보겠습니다:

  1. 새로운 요청이 거절되는 조건
현재 상태:
- 최대 스레드(200개) 모두 사용 중
- 대기 큐(accept-count=100)도 가득 참
= 새로운 요청에 대해 connection refused 발생
  1. 이미 처리 중인 요청은?
  • 안전하게 계속 처리됨
  • 각자의 스레드에서 작업 완료까지 정상 실행
  • 작업 완료 후 스레드는 다시 Pool로 반환
@GetMapping("/api")
public String handleRequest() {
    // 이미 실행 중인 이 코드는
    // 서버가 새 요청을 거절하더라도
    // 정상적으로 끝까지 실행됨
    heavyTask();
    return "done";
}

이것이 Thread Pool의 중요한 장점입니다:

  • 시스템 부하 한계를 초과하는 요청은 빠르게 거절
  • 이미 수락한 요청의 안정성은 보장
  • 전체 시스템의 안정성 유지

추가로 Java 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는 빠르게 반환
    }
}

Java에서 제공하는 Thread Pool의 종류는 무엇이 있나요?

1. Fixed Thread Pool

ExecutorService executor = Executors.newFixedThreadPool(5);
  • 고정된 수의 스레드 유지
  • 기본값: 지정한 수의 스레드, 무제한 큐
  • 안정적인 수의 스레드가 필요할 때 사용

2. Cached Thread Pool

ExecutorService executor = Executors.newCachedThreadPool();
  • 필요에 따라 스레드 생성/제거
  • 기본값: 코어 스레드 0개, 최대 Integer.MAX_VALUE
  • 짧은 작업이 많은 경우에 적합

3. Scheduled Thread Pool

ScheduledExecutorService executor = Executors.newScheduledThreadPool(5);
  • 주기적 작업 실행용
  • 기본값: 지정한 코어 스레드, 최대 Integer.MAX_VALUE
  • 정해진 시간에 실행해야 하는 작업에 사용

Tomcat Thread Pool과 별도 Thread Pool의 역할

  1. Tomcat Thread Pool
  • HTTP 요청 접수 및 응답 반환
  • 웹 요청 처리에 최적화
  • 설정은 application.yml에서 관리
  1. 별도의 Java Thread Pool
  • 긴 작업 처리용
  • 특정 비즈니스 로직에 최적화
  • 코드에서 직접 생성 및 관리

실제 사용 시 주의할 점

  1. Tomcat Thread Pool과 별도 Thread Pool의 관계
  • Tomcat Thread는 요청 접수 담당
  • 별도 Thread Pool은 실제 작업 처리 담당
  • 두 Pool은 독립적으로 동작
  1. 리소스 관리
  • Thread Pool 크기는 CPU 코어 수를 고려하여 설정
  • 메모리 사용량 고려 (스레드 당 약 1MB)
  • 적절한 큐 사이즈 설정

이렇게 Spring Boot의 기본 Thread Pool과 추가로 사용할 수 있는 Java Thread Pool을 적절히 활용하면, 효율적이고 안정적인 멀티스레딩 애플리케이션을 구현할 수 있습니다.


이상으로 스프링 부트 서버의 Thread Pool에 대해 알아보았습니다.

0개의 댓글