부하 테스트 에러: java.net.BindException

이상윤·2026년 3월 19일

CS

목록 보기
4/4

Java 21의 ZGC와 G1GC 성능을 비교하기 위해 로컬 환경(macOS)에서 부하 테스트를 진행하던 중, 기이한 현상을 발견했다. 서버는 문제가 없는데 JMeter에서는 수십만 건의 에러가 발생한 것이다.

테스트 시나리오는 다음과 같이 설정하였고, 로그인을 진행하는 간단한 api였다.

Thread 100, Duration 300s, Ramp-up 30s

테스트 종료 후 86%에 달하는 에러가 발생해 JMeter 결과 파일을 살펴보니, 다음 로그가 많이 찍혀있었다.

1771728085116,53,POST /api/user/login,200,,Login Thread Group 1-9,text,true,,1121,237,33,33,http://localhost:8080/api/user/login,53,0,0
1771728085165,4,POST /api/user/login,Non HTTP response code: java.net.BindException,Non HTTP response message: Can't assign requested address,Login Thread Group 1-31,text,false,,2298,0,33,33,http://localhost:8080/api/user/login,0,0,4
1771728085161,9,POST /api/user/login,Non HTTP response code: java.net.BindException,Non HTTP response message: Can't assign requested address,Login Thread Group 1-12,text,false,,2298,0,33,33,http://localhost:8080/api/user/login,0,0,9
1771728085167,3,POST /api/user/login,Non HTTP response code: java.net.BindException,Non HTTP response message: Can't assign requested address,Login Thread Group 1-17,text,false,,2298,0,33,33,http://localhost:8080/api/user/login,0,0,3
1771728085165,5,POST /api/user/login,Non HTTP response code: java.net.BindException,Non HTTP response message: Can't assign requested address,Login Thread Group 1-7,text,false,,2298,0,33,33,http://localhost:8080/api/user/login,0,0,5
1771728085168,2,POST /api/user/login,Non HTTP response code: java.net.BindException,Non HTTP response message: Can't assign requested address,Login Thread Group 1-21,text,false,,2298,0,33,33,http://localhost:8080/api/user/login,0,0,2

초반에는 맨 위 로그와 같이 200이 계속 찍혔었지만, 어느 구간부터는 BindException이 계속 찍히면서, 에러를 발생시키고 있었던 것이다.

인터넷에 다음 에러를 검색 해 보니 이미 같은 문제를 겪은 사람들이 많았고, 이 에러는 TCP 요청에 대한 포트를 할당하지 못해 생긴 문제이다.

이제 이 문제가 생긴 근본적인 이유를 알아보자.

HTTP 요청 중 TCP 통신을 할 때, 클라이언트는 서버에 접속하기 위해 OS로부터 임시 포트를 할당받는다.

테스트 중 JMeter에서 100개의 쓰레드가 300초 동안 끊임없이 HTTP 요청을 보내게 되는데, 이때 각 요청이 완료되면 해당 소켓을 닫으려 한다. 하지만 TCP 프로토콜 설계상 소켓은 즉시 해제되지 않고 TIME_WAIT 상태에 머무르게 되어 소켓을 계속 소유하게된다.

macOS의 기본 설정은 사용할 수 있는 임시 포트 범위가 약 16,000개 정도로 좁고, TIME_WAIT 지속 시간은 길기 때문에 시작하고 얼마 지나지 않아 가용 포트가 고갈된다. 이로 인해 BindException이 발생하게 되는 것이다.

이러한 문제의 해결책은 크게 두 가지가 있다.

  1. TCP 세그먼트가 네트워크상에 존재할 수 있는 시간 자체를 줄인다.
    msl(Maximum Segment Lifetime)은 TCP 세그먼트가 네트워크상에 존재할 수 있는 최대 시간을 의미하며, 보통 TIME_WAIT 상태는 2×MSL2 \times MSL 동안 유지된다.
    하지만 너무 극단적으로 낮추면, 지연되었던 이전 연결의 패킷이 뒤늦게 도착했을 때 새로운 연결의 패킷으로 오인받아 데이터 무결성 문제가 생길 수 있어 사용에 주의하여야 한다.
    또한, 이와 함께 사용할 수 있는 포트의 번호 자체를 늘려 포트 풀을 늘여도 효과가 있을 것이다.

  2. JMeter의 Connection Reuse를 활성화한다.
    이 설정은 HTTP 요청마다 새로운 소켓을 열지 않고, 기존 연결을 재사용하도록 설정하는 것이며, 포트 사용량을 획기적으로 줄일 수 있다.

필자는 첫 번째 방법을 사용했고, 결과로 다음과 같이 잘 실행된 것을 볼 수 있다.

0개의 댓글