장난감 연구소

[Spring] 스프링 부트, 요청이 몰리면 어떻게 될까? 본문

개발/Spring

[Spring] 스프링 부트, 요청이 몰리면 어떻게 될까?

changi1122 2025. 4. 9. 17:39
    스프링 부트 애플리케이션에 대규모 요청이 몰리게 되면 어떻게 될까?

    스프링 부트를 사용하지만, 앞단에서 요청이 어떻게 처리되는지 모르고 있었다. 스프링 부트에 대규모 요청이 몰리게 되면 어떻게 될까? 그런 물음을 받았을 때 내가 납득할 수 있는 대답을 낼 수 없었다. 일단 요청마다 다른 스레드가 처리한다는 건 알겠는데, 요청마다 새로운 스레드가 만들어지는지, 그렇다면 최대 몇 개까지 생성될 수 있는지, 또 처리할 수 없는 요청은 어떻게 되는지 의문이 생겼다.

    이번 글에서는 스프링 부트의 임베디드 톰캣과 관련된 설정을 정리하였다.

    관련 설정 요소

    톰캣 커넥션과 스레드 구조

    커넥션 (Connections)

    HTTP 통신은 TCP 연결 위에서 이뤄진다. 클라이언트가 요청을 보내면 톰캣은 소켓을 열어 해당 연결을 유지한다. 이와 관련된 설정이 가능하다.

     

    • max-connections를 통해 동시에 처리할 수 있는 최대 TCP 연결 수를 설정할 수 있다. 기본 값은 8192 이며, 처리 중이거나 keep-alive 상태인지와 관계 없이 열린 소켓 수가 이 값에 도달하면, 그 이후 연결은 accept-count 큐로 들어간다.
    • accept-count들어오는 요청이 대기할 수 있는 최대 큐 길이로, 기본 값은 100이다. 이때 큐는 운영체제의 TCP 대기 큐(backlog)로 소켓 프로그램이 accept 함수를 호출하여 요청을 받기 전에 수신 대기 중인 연결 요청을 저장하는 큐이다.

    accept-count 값을 넘어서는 요청은 Connection refused 오류를 응답으로 받는다.

     

     스레드 풀 (Thread Pool)

    가장 먼저 얘기한 의문에 답변하자면 톰캣은 요청마다 새 스레드를 만들지 않는다.

    요청 하나를 처리할 때마다 새 스레드를 만든다면 오버헤드가 크다. 그래서 톰캣은 요청을 처리하기 위해 스레드 풀에 미리 일정 수의 스레드를 만들어 두고, 요청이 올 때마다 꺼내 사용한다. application.yml에서 스레드 풀에 대한 설정도 가능하다.

    • threads.max 스레드 풀의 최대 스레드 수이며, 기본 값은 200이다.
    • thread.min-spare스레드 풀의 최소 스레드 수로, 기본 값은 10이다.

    Spring boot 3.3 부터는 threads.max-queue-capacity라는 설정이 추가되었다.

    • threads.max-queue-capacity스레드 풀의 대기 큐의 최대 용량으로, 스레드 풀의 모든 스레드가 사용 중일 때 요청을 대기시킬 수 있는 큐의 최대 길이를 설정할 수 있다. 기본 값은 INT_MAX(2147483647)이다.

    따라서 정리하자면, 요청이 몰리게 될 때 스레드 풀이 최대치까지 동시에 요청을 처리할 수 있고, threads.max + threads.max-queue-capacity를 넘어서는 요청은 오류를 응답한다.

     

    application.yml 설정 예시

    위 설정 값들을 설정하기 위한 application.yml 파일 예시는 아래와 같다.

    server:
      tomcat:
        max-connections: 8192 # 동시에 유지할 수 있는 최대 연결 수 (소켓 수)
        accept-count: 100     # max-connections 초과 시 대기할 수 있는 연결 요청 수
        threads:
          max: 200            # 동시에 요청을 처리할 수 있는 최대 스레드 수
          min-spare: 10       # 항상 유지하는 최소 예비 스레드 수
          max-queue-capacity: 2147483647 # 스레드가 모두 사용 중 요청을 대기시킬 큐의 최대 크기

     

    테스트

    실제 위 설정들이 효과있는지 테스트해보자. 병목을 쉽게 만들기 위해 스레드 풀과 커넥션 설정 값을 낮게 설정하고, JMeter를 사용하여 요청을 보냈다.

    Spring boot 프로젝트 생성

    • application.yml
    server:
      tomcat:
        threads:
          max: 10                # Thread Pool 테스트시에만 이 값으로 설정
          max-queue-capacity: 5  # Thread Pool 테스트시에만 이 값으로 설정  
        max-connections: 10      # Connection 테스트시에만 이 값으로 설정
        accept-count: 5          # Connection 테스트시에만 이 값으로 설정

     

    • TestController

    /test로 GET 요청시 10초 대기 후 응답을 보내는 테스트용 컨트롤러 메서드를 작성하였다.

    @RestController
    public class TestController {
        @GetMapping("/test")
        public String test() throws InterruptedException {
            Thread.sleep(10000); // 10초 대기
            return "Done";
        }
    }

     

    커넥션 설정 테스트 결과

    왼쪽부터 커넥션 설정 테스트의 9개 요청 결과, 11개 요청 결과, 16개 요청 결과

    커넥션 설정 값만 제한한 후 JMeter로 테스트한 결과는 아래와 같다.

    • 9개 요청시, 모두 성공 (소요시간 10초)
    • 11개 요청시, 모두 성공 (소요시간 약 20초) = 10개 요청 먼저 처리, 1개 요청 accept-count 큐 대기
    • 16개 요청시, 1개 요청 실패, 15개 성공 (소요시간 약 20초) = 1개 요청은 accept-count 큐에 대기하지 못하고 실패

    위와 같이 10개 요청이 먼저 처리되고, accept-count 값 만큼 5개 요청이 대기되는 걸 확인할 수 있었다. (10+5)개를 넘어서는 요청 1개는 실패하였다.

     

    스레드 풀 설정 테스트 결과

    왼쪽부터 스레드 풀 설정 테스트의 9개 요청 결과, 11개 요청 결과, 16개 요청 결과
    스레드 풀 대기 큐 초과시 출력되는 로그

    스레드 풀 설정 값만 제한한 후 JMeter로 테스트한 결과는 아래와 같다.

    • 9개 요청시, 모두 성공 (소요시간 10초)
    • 11개 요청시, 모두 성공 (소요시간 약 20초) = 10개 요청 먼저 처리, 1개 요청 스레드 풀 대기 큐 대기
    • 16개 요청시, 1개 요청 실패, 15개 성공 (소요시간 약 20초) = 1개 요청은 스레드 풀 대기 큐에 대기하지 못하고 실패

    위와 같이 스레드 풀 설정에서도 thread.max 값 만큼 10개 요청이 먼저 실행되고, 최대 threads.max-queue-capacity 값인 5개까지 대기되는 걸 확인할 수 있었다.

     

    결론

    동시에 톰캣 설정을 통해 동시에 처리되는 TCP 연결 수와 대기할 수 있는 길이를 설정하고, 스레드 풀 관련 설정에서 동시에 실행할 스레드의 최대 수와 대기할 수 있는 요청 수를 설정할 수 있었다.

    정리하면 먼저 최대 연결 수 제한과 대기 연결 수 제한이 있다. 그리고 각 연결에 대해서 스레드 풀에서 스레드를 가져와 사용하는 구조이며, 스레드 수 제한과 스레드에 대기할 수 있는 요청의 수 제한(Spring boot 3.3에서 추가)이 있다.

    스프링 부트 프로젝트에 요청이 몰린다면 이 값들에 따라서 요청이 처리/대기/실패한다고 고려해도 좋을 걸로 보인다.

     

    의문점

    실패한 요청의 응답으로 Java Exception stack trace 결과가 표시되는데, 응답에 헤더가 없고, 503 상태코드도 없는 걸로 보이는데, 원래 이런지 잘 모르겠다.

     

    참고자료

    [Spring] SpringBoot의 Tomcat 설정 알아보기 (feat. Thread, Thread Pool) — 성장하는 성하 Blog

    Common Application Properties :: Spring Boot

    Tomcat Performance Best Practices

    '개발 > Spring' 카테고리의 다른 글

    [개발] 최근 프로젝트에서 고민한 문제들  (0) 2025.09.19