[Performance Tuning] Nginx-Tomcat 최적화: 커넥션 풀(Connection Pool)의 임계값을 찾는 법

 웹 서비스 성능의 병목은 대부분 '연결'에서 발생한다. 클라이언트의 요청을 받는 Nginx와 비즈니스 로직을 처리하는 Tomcat 사이의 통신 효율을 극대화하지 못하면, 서버 자원이 풍부해도 응답 속도는 처참해질 수 있다. 17년 차 아키텍트의 관점에서, 실전 환경의 부하를 견디는 Nginx-Tomcat 커넥션 풀 튜닝의 핵심 팩트를 정리한다.


1. Nginx: 업스트림 커넥션 유지 (Keepalive)

기본적으로 Nginx는 Tomcat(Upstream)으로 요청을 보낼 때마다 매번 새로운 커넥션을 맺고 끊는 'Short-lived connection' 방식을 사용한다. 이는 고부하 상황에서 TCP 3-Way Handshake 비용과 TIME_WAIT 소켓 고갈 문제를 야기한다.

  • Keepalive 설정: Nginx에서 Tomcat으로의 연결을 유지하여 소켓 재사용률을 높여야 한다.

    Nginx
    upstream tomcat_cluster {
        server 127.0.0.1:8080;
        keepalive 32; # 연결을 유지할 최대 커넥션 수
    }
    
  • Proxy Protocol 준수: proxy_http_version 1.1;proxy_set_header Connection ""; 설정을 병행해야 HTTP/1.1의 Keepalive 특성을 온전히 활용할 수 있다.


2. Tomcat: Connector와 Thread Pool 최적화

Tomcat은 클라이언트(Nginx)의 요청을 받는 Acceptor와 실제 로직을 수행하는 Worker Thread로 구성된다.

  • maxThreads: 동시에 처리할 수 있는 최대 요청 수다. 무작정 높이면 Context Switching 비용으로 인해 CPU 부하만 가중된다. 보통 CPU 코어 수와 I/O 대기 시간을 고려해 결정한다.

  • acceptCount: 모든 maxThreads가 점유되었을 때 OS 레벨에서 대기시킬 요청 큐(Queue)의 길이다. 이 수치가 너무 크면 클라이언트는 타임아웃에 빠지고, 너무 작으면 Connection Refused 에러가 발생한다.

  • maxConnections: 특정 시점에 서버가 유지할 수 있는 최대 연결 수다. NIO 방식을 사용할 경우 maxThreads보다 훨씬 크게 설정하여 비활성 커넥션을 효율적으로 관리할 수 있다.


3. HikariCP: DB 커넥션 풀의 'Golden Rule'

Java 진영의 표준인 HikariCP 설정은 전체 시스템 성능의 '라스트 마일'이다.

  • 최적의 Pool Size 공식:

    $$pool size = T_n \times (C_m - 1) + 1$$

    (여기서 $T_n$은 최대 스레드 수, $C_m$은 단일 쿼리 시 필요한 최대 커넥션 수다. 하지만 실무에서는 보통 (Core count * 2) + effective_spindle_count를 가이드라인으로 삼는다.)

  • minimumIdle vs maximumPoolSize: 성능을 위해서는 두 값을 동일하게 설정하여 커넥션 풀의 크기를 고정(Fixed)하는 것을 권장한다. 커넥션을 유동적으로 늘리고 줄이는 과정 자체가 런타임 오버헤드이기 때문이다.


4. 실전 튜닝 체크리스트 (Fact Check)

대상주요 설정 항목권장 전략
Nginxworker_connections프로세스당 최소 1024 이상, 시스템 ulimit 고려
Nginxkeepalive_requests하나의 Keepalive 연결로 처리할 요청 수 (기본 100 -> 1000 이상 권장)
TomcatminSpareThreads상시 대기 스레드 수, 트래픽 급증 대비 최소 10~25 유지
TomcatconnectionTimeout20000(20초)보다 짧게 설정하여 좀비 커넥션 방지
OSnet.ipv4.tcp_tw_reuse1로 설정하여 TIME_WAIT 소켓 재사용 허용 (리눅스 커널 튜닝)

5. 아키텍트의 결론: "모니터링 없는 튜닝은 추측일 뿐이다"

설정값에 정답은 없다. **nari (Netstat)**나 VisualVM, 혹은 Prometheus/Grafana를 통해 현재 활성 스레드 수(Active Threads)와 커넥션 사용률을 모니터링하며 점진적으로 최적값을 찾아야 한다. 특히 앞서 언급한 **가상 스레드(Virtual Thread)**를 적용한다면, 기존의 maxThreads 공식은 완전히 재설계되어야 함을 명심하라.

댓글

이 블로그의 인기 게시물

주말 일요일, SI 프로젝트 출근 현장 이야기

담합 신고포상금 완전 가이드 2026 | 로또보다 담합 신고가 낫다

카드 포인트 현금화 완전 가이드 2026 | 흩어진 포인트를 내 계좌로