이 문서를 통해 아래 질문들에 답할 수 있게 됩니다.

  • Connection State와 TIME_WAIT에서 붙잡아야 할 핵심 판단 기준은 무엇인가?
  • connection state는 네트워크의 thread dump다라는 기준은 어떤 JVM/OS 신호로 확인되는가?
  • Java/Spring 코드에서 WebClient, RestTemplate, JDBC driver의 connection reuse 정책은 TIME_WAITESTABLISHED 분포에 직접 영… 흐름은 어떤 장애 신호로 드러나는가?

개요

이 문서의 주제는 Connection State와 TIME_WAIT이다. TCP connection state와 TIME_WAIT이 백엔드 장애에서 어떤 신호인지 정리한다.

먼저 붙잡을 기준은 다음 문장이다. connection state는 네트워크의 thread dump다. ESTABLISHED, TIME_WAIT, CLOSE_WAIT, SYN-RECV는 연결이 어느 단계에서 머무는지 보여 주며, 상태별 의미를 섞으면 정상 종료와 connection leak을 혼동하게 된다.

핵심 모델

  • 판단 기준: connection state는 네트워크의 thread dump다.
  • TCP state는 연결의 현재 위치를 보여 주는 운영 증거다.
  • TIME_WAIT은 정상 종료 후 지연 상태이며, 무조건 장애가 아니라 짧은 연결 반복의 양을 보여 준다.
  • CLOSE_WAIT은 상대가 닫았는데 application이 socket을 닫지 않은 상태라 leak 신호가 될 수 있다.
  • SYN-RECV는 handshake나 backlog 압박을, ESTABLISHED 과다는 keep-alive, 폭주, connection pool 문제를 의심하게 한다.
  • State는 전체 수보다 local/remote endpoint, 증가 속도, timeout 설정과 함께 해석한다.

문서별 핵심 구분

  • connection state는 TCP 연결의 현재 위치를 보여 준다.
  • TIME_WAIT은 정상 동작이지만 과도하면 연결 churn 신호다.
  • ESTABLISHED 증가는 keep-alive나 leak, 폭주를 의심하게 한다.

원리

TCP 연결은 생성, 사용, 종료 과정에서 상태가 바뀐다. 장애 중 ss -tan이 보여 주는 state는 연결이 어디에서 많이 머무는지 알려 주는 snapshot이다.

TIME_WAIT은 먼저 연결 종료를 수행한 쪽이 일정 시간 동안 남기는 정상 상태다. 개수가 많다는 사실만으로 장애는 아니지만, 짧은 HTTP 연결이 반복되거나 keep-alive가 꺼져 있거나 client pool 재사용이 안 되는 신호일 수 있다.

CLOSE_WAIT은 의미가 다르다. 상대가 연결을 닫았는데 application이 socket을 닫지 못한 상태이므로, HTTP client response body 미해제, stream close 누락, library bug 같은 application 쪽 누수를 의심한다.

따라서 state 해석은 “많다/적다”가 아니라 어떤 state가 어느 endpoint에서 증가했고, 그 시점의 client reuse, timeout, pool 설정이 무엇이었는지로 좁혀야 한다.

Java/Spring 연결

  • WebClient, RestTemplate, JDBC driver의 connection reuse 정책은 TIME_WAITESTABLISHED 분포에 직접 영향을 준다.
  • Response body를 끝까지 소비하지 않거나 stream close를 누락하면 CLOSE_WAIT 증가로 드러날 수 있다.
  • Server-side keep-alive timeout과 client pool idle timeout이 어긋나면 정상 트래픽에서도 connection churn이 늘어난다.
  • Actuator의 client pool metric, Tomcat connection metric, ss state count를 같은 시간축으로 비교한다.
  • State가 outbound remote address에 몰려 있으면 Spring Controller보다 외부 호출 코드와 client pool 설정을 먼저 본다.
  • Spring 코드 리뷰에서는 Connection State와 TIME_WAIT 판단 기준이 어떤 thread, pool, timeout, resource 설정으로 드러나는지 확인한다.

코드와 설정 예시

ss -tan state time-wait | wc -l
ss -tan state close-wait | head
ss -tan state established '( sport = :8080 )' | head

예시는 state를 한꺼번에 세는 것보다 상태별 의미를 나누는 방식이다. TIME_WAIT은 churn, CLOSE_WAIT은 close 누락, ESTABLISHED는 유지 중인 연결을 먼저 의심하고, local/remote address로 inbound와 outbound를 분리한다.

관찰 명령

ss -tan state time-wait | wc -l
ss -tan state established | wc -l
ss -s

Connection State와 TIME_WAIT 관찰 명령은 결론이 아니라 가설 확인 도구다. 운영 중에는 반복 간격, 대상 PID, 수집 시각을 함께 남기고 출력이 큰 명령은 범위를 제한한다.

지표 해석

신호먼저 의심할 것다음 확인
TIME_WAIT 급증짧은 연결 반복, keep-alive 미사용client/server connection reuse
CLOSE_WAIT 증가application close 누락thread dump, HTTP client 사용 코드
SYN-RECV 증가handshake 지연, backlog 압박ss state syn-recv, LB 로그
ESTABLISHED 과다keep-alive, 폭주, connection pool 문제endpoint별 분포, pool metric

Connection State와 TIME_WAIT에서는 핵심 신호와 보조 지표를 같은 시간축에서 맞춘다. 먼저 변한 신호와 뒤따라 포화된 신호를 분리해야 원인과 결과를 구분할 수 있다.

장애 상황에서 보는 순서

  1. ss -s로 전체 state 분포를 보고 어느 상태가 튀는지 고른다.
  2. 문제 state를 local port와 remote address 기준으로 inbound/outbound로 나눈다.
  3. TIME_WAIT이면 connection reuse, keep-alive, client/LB timeout 설정을 확인한다.
  4. CLOSE_WAIT이면 application이 response body나 stream을 닫는지 코드와 thread dump를 본다.
  5. ESTABLISHED가 많으면 정상 keep-alive인지 pool leak인지 Actuator와 client pool metric으로 확인한다.

이 순서는 Connection State와 TIME_WAIT 후보를 빠르게 좁히기 위한 기본 루틴이다. 결론을 내리기 전에는 애플리케이션 증거와 JVM/OS 증거를 최소 한 번 이상 맞춘다.

실전 팁

  • TIME_WAIT을 줄이는 sysctl부터 찾기 전에 왜 짧은 연결이 반복되는지 본다.
  • CLOSE_WAIT은 네트워크가 아니라 application close 누락일 수 있으므로 코드 경로를 추적한다.
  • State 수는 snapshot이므로 10초 간격으로 여러 번 재서 증가 속도를 본다.
  • 같은 ESTABLISHED라도 inbound client 연결과 outbound DB/HTTP 연결을 분리한다.
  • 장애 회고에는 state 이름만 쓰지 말고 local/remote endpoint와 timeout 설정을 함께 남긴다.

주니어 팁

  • TIME_WAIT은 “나쁜 상태”가 아니라 정상 종료 후 잠시 남는 상태라는 점부터 구분한다.
  • CLOSE_WAIT이 많으면 상대가 닫은 뒤 내 애플리케이션이 닫지 않은 상태라는 뜻을 기억한다.
  • ss -tan을 볼 때 state와 local/remote address를 한 줄에서 함께 읽는다.

시니어 팁

  • Connection reuse 정책은 server timeout, LB timeout, client pool TTL이 서로 어긋나지 않게 설계한다.
  • State별 임계값은 절대 수보다 평시 대비 증가율과 endpoint별 편중으로 잡는다.
  • TIME_WAIT 완화 조치보다 client 재사용, keep-alive, timeout 예산 조정이 먼저인지 판단한다.

Guru급 팁

  • Connection State와 TIME_WAIT 주제는 단일 snapshot보다 변화 방향이 중요하다. 같은 명령을 간격을 두고 실행해 상태가 증가, 유지, 회복 중 어디인지 본다.
  • 고급 도구는 Connection State와 TIME_WAIT 가설을 좁힐 때만 사용하고 기본 지표와 모순되는지 먼저 확인한다.
  • Connection State와 TIME_WAIT 조치 후에는 CPU, memory, socket, disk, pool 중 다음 포화 지점이 어디로 이동했는지 확인한다.

위험 신호!

  • TIME_WAIT 숫자만 보고 kernel tuning으로 바로 들어간다.
  • CLOSE_WAIT을 정상 종료 상태로 오해하고 application close 누락을 놓친다.
  • State 총합만 세고 어느 remote endpoint가 증가했는지 남기지 않는다.
  • Inbound state와 outbound state를 합쳐 보고 Tomcat 문제로 단정한다.
  • 재시작 후 state가 사라졌다는 이유로 connection churn 원인을 기록하지 않는다.

확인 질문

확인 질문

  • TIME_WAIT이 많다는 사실만으로 장애라고 단정하면 안 되는 이유는 무엇인가?
    • TIME_WAIT은 TCP 정상 종료 후 남는 상태이며, 문제 여부는 증가 속도와 connection churn, port 재사용 압박을 함께 봐야 하기 때문이다.
  • CLOSE_WAIT이 늘면 어떤 코드 문제를 의심해야 하는가?
    • Application이 socket, stream, response body를 닫지 못해 연결 close를 완료하지 않는 경로를 의심해야 한다.
  • Connection state를 장애 보고서에 남길 때 무엇을 같이 적어야 하는가?
    • state별 수, local/remote endpoint, 증가 시간대, timeout/keep-alive 설정, inbound/outbound 구분을 같이 적어야 한다.

참고 문서