이 문서를 통해 아래 질문들에 답할 수 있게 됩니다.
Listen Backlog와 Accept Queue에서 붙잡아야 할 핵심 판단 기준은 무엇인가?요청은 Controller 이전에도 줄을 선다라는 기준은 어떤 JVM/OS 신호로 확인되는가?- Java/Spring 코드에서
Listen Backlog와 Accept Queue판단 기준은 어떤 장애 신호로 드러나는가?
개요
이 문서의 주제는 Listen Backlog와 Accept Queue이다. 서버가 연결을 받는 과정에서 backlog와 accept queue가 만드는 병목을 정리한다.
먼저 붙잡을 기준은 다음 문장이다. 요청은 Controller 이전에도 줄을 선다. Spring 로그에 아무것도 남지 않았는데 client timeout이 발생한다면, 요청은 Java handler가 아니라 SYN queue, accept queue, connector 경계에서 대기했을 수 있다. 따라서 backlog 문제는 애플리케이션 에러 로그보다 OS queue와 Tomcat accept 지표를 먼저 맞춰 봐야 한다.
핵심 모델
- 판단 기준: 요청은 Controller 이전에도 줄을 선다.
- Client 연결은 handshake 단계와 accept 단계에서 각각 다른 queue를 지난다.
- SYN queue는 TCP handshake가 완료되기 전의 대기이고, accept queue는 완료된 연결이 application accept를 기다리는 대기다.
- Tomcat
acceptCount는 worker thread가 모두 바쁠 때 추가 연결을 얼마나 대기시킬지와 연결된다. - Queue가 밀리면 Controller 로그 없이 connection timeout, reset, LB 502/504로 보일 수 있다.
- Backlog를 키우는 것은 완충 시간을 늘리는 조치이지, 느린 handler나 부족한 worker thread를 해결하는 조치가 아니다.
문서별 핵심 구분
- 요청은 Controller 전에 SYN queue와 accept queue에서 대기할 수 있다.
- acceptCount와 backlog는 Java thread 이전의 완충 지점이다.
- queue drop은 애플리케이션 로그 없이 client timeout으로 보일 수 있다.
원리
서버가 listen 상태가 되면 OS는 새 연결을 바로 Controller로 넘기지 않는다. 먼저 handshake를 처리하고, 완료된 연결을 accept 가능한 queue에 넣은 뒤, Tomcat connector가 이를 받아 worker thread로 넘긴다.
SYN queue가 밀리면 handshake 자체가 지연된다. Accept queue가 밀리면 TCP 연결은 성립했지만 application이 아직 받지 못한 상태가 된다. 두 경우 모두 Spring MVC interceptor나 Controller 로그에는 흔적이 없을 수 있다.
Tomcat의 acceptCount는 모든 request processing thread가 사용 중일 때 운영체제 queue에 추가로 대기시킬 연결 수와 관련된다. maxThreads가 포화된 상태에서 acceptCount만 크게 늘리면 실패가 늦게 보일 뿐 응답 지연은 더 길어질 수 있다.
따라서 backlog 장애는 “연결을 더 많이 받아야 한다”가 아니라 “어디서 줄을 서고, 줄을 서도 처리할 downstream capacity가 있는가”로 판단한다.
Java/Spring 연결
- Backlog에서 밀린 요청은 Spring filter, interceptor, Controller에 도달하지 못하므로 application log가 비어 있을 수 있다.
server.tomcat.accept-count는 worker thread가 꽉 찼을 때 연결을 얼마나 더 대기시킬지와 연결된다.- Actuator의 Tomcat busy thread와 current connection을 LB timeout,
sslisten queue와 같은 시간대에 맞춰 본다. - Handler가 느려 worker가 포화된 경우에는 backlog 조정보다 endpoint latency와 downstream pool을 먼저 봐야 한다.
- Client timeout이 짧고
accept-count가 크면 queue에서 기다리다가 client가 먼저 끊는 상황이 늘 수 있다. - Spring 코드 리뷰에서는
Listen Backlog와 Accept Queue판단 기준이 어떤 thread, pool, timeout, resource 설정으로 드러나는지 확인한다.
코드와 설정 예시
server.tomcat.threads.max=200
server.tomcat.accept-count=100
server.connection-timeout=5s이 설정은 thread pool과 accept 대기열을 함께 본다는 점이 핵심이다. threads.max가 이미 포화되어 있다면 accept-count를 늘려도 요청은 더 오래 기다릴 뿐이고, connection-timeout이 너무 길면 느린 client가 connector 자원을 오래 잡는다.
관찰 명령
ss -ltnp
ss -tan state syn-recv
cat /proc/net/netstat | grep -E 'Listen|TCP'Listen Backlog와 Accept Queue 관찰 명령은 결론이 아니라 가설 확인 도구다. 운영 중에는 반복 간격, 대상 PID, 수집 시각을 함께 남기고 출력이 큰 명령은 범위를 제한한다.
지표 해석
| 신호 | 먼저 의심할 것 | 다음 확인 |
|---|---|---|
| SYN-RECV 증가 | handshake 지연, SYN queue 압박 | ss state syn-recv, LB 로그 |
| ListenOverflows/ListenDrops 증가 | accept queue overflow | /proc/net/netstat, kernel counter |
| Controller 로그 없이 timeout | Controller 이전 대기 | LB access log, Tomcat connection metric |
| busy thread 100%와 backlog 증가 | accept 이후 처리 지연 | thread dump, slow downstream |
Listen Backlog와 Accept Queue에서는 핵심 신호와 보조 지표를 같은 시간축에서 맞춘다. 먼저 변한 신호와 뒤따라 포화된 신호를 분리해야 원인과 결과를 구분할 수 있다.
장애 상황에서 보는 순서
- 장애 시간대 client timeout과 LB 502/504가 Controller 로그보다 먼저 발생했는지 확인한다.
ss -ltnp로 listen socket의Recv-Q와 대상 PID를 확인한다.ss -tan state syn-recv와/proc/net/netstat의 listen overflow/drop counter를 본다.- Tomcat busy thread, current connection, request 처리 시간을 같은 시간대로 맞춘다.
acceptCount를 조정하기 전에 handler 지연, downstream timeout, worker thread 포화를 확인한다.
이 순서는 Listen Backlog와 Accept Queue 후보를 빠르게 좁히기 위한 기본 루틴이다. 결론을 내리기 전에는 애플리케이션 증거와 JVM/OS 증거를 최소 한 번 이상 맞춘다.
실전 팁
- Controller 로그가 없다는 사실은 “요청이 없었다”가 아니라 “Controller까지 못 왔다”일 수 있다.
acceptCount를 늘릴 때는 client timeout, LB timeout, Tomcat timeout이 서로 맞는지 같이 본다.ss의 listen queue snapshot은 순간값이므로 overflow/drop counter처럼 누적값과 함께 봐야 한다.- Backlog를 키운 뒤에도 busy thread가 계속 100%라면 처리 속도 병목은 그대로 남아 있다.
- 장애 회고에는 queue가 밀린 위치와 함께 그 시점의 worker thread, downstream 지표를 남긴다.
주니어 팁
- Spring 로그가 비어 있어도 요청이 없었다고 단정하지 말고 “Controller 이전 대기열”을 확인한다.
- client timeout이 있는데 서버 로그가 조용하면
ss와 LB 로그를 먼저 확인한다. - Queue 숫자는 단독으로 보지 말고 busy thread와 request 처리 시간을 같이 본다.
시니어 팁
acceptCount는 서비스가 버틸 수 있는 대기 시간과 client timeout을 기준으로 산정한다.- Backlog overflow가 반복되면 서버 수 증설보다 먼저 느린 endpoint, downstream pool, retry 폭증을 확인한다.
- LB, kernel backlog, Tomcat connector, application timeout을 한 장의 표로 맞춰 운영 기준을 만든다.
Guru급 팁
Listen Backlog와 Accept Queue주제는 단일 snapshot보다 변화 방향이 중요하다. 같은 명령을 간격을 두고 실행해 상태가 증가, 유지, 회복 중 어디인지 본다.- 고급 도구는
Listen Backlog와 Accept Queue가설을 좁힐 때만 사용하고 기본 지표와 모순되는지 먼저 확인한다. Listen Backlog와 Accept Queue조치 후에는 CPU, memory, socket, disk, pool 중 다음 포화 지점이 어디로 이동했는지 확인한다.
위험 신호!
- Controller 로그가 없다는 이유로 네트워크 장애를 배제한다.
acceptCount를 크게 잡으면 장애가 해결된다고 생각한다.Recv-Qsnapshot 하나만 보고 overflow가 없었다고 결론낸다.- queue overflow와 worker thread 포화를 구분하지 않고 모두 “트래픽 증가”로 기록한다.
- backlog 조정 후 client timeout과 대기 시간이 어떻게 바뀌었는지 확인하지 않는다.
확인 질문
확인 질문
- Controller 로그 없이 client timeout이 날 때 backlog를 의심하는 이유는 무엇인가?
- 연결이 SYN queue나 accept queue에서 대기하면 Spring handler까지 도달하지 못해 애플리케이션 로그가 없을 수 있기 때문이다.
acceptCount를 키우기 전에 어떤 지표를 같이 봐야 하는가?
- Tomcat busy thread, request 처리 시간, downstream pool, client/LB timeout, listen overflow/drop counter를 함께 봐야 한다.
- Backlog overflow가 장애 보고서에 남아야 하는 이유는 무엇인가?
- 단순 트래픽 증가인지, Java thread 포화 때문에 accept가 늦어진 것인지, timeout 정책 불일치인지 다음 조치가 달라지기 때문이다.