HTTP Keepalive 연결 및 웹 성능
여러분이 관찰하고 있는 것은 “HTTP heavy lifting”이라고 부를 수 있는 효과 중 하나입니다. 이 포스트에서는 HTTP Keepalive 가 작동하는 방식과 일반적인 HTTP 서버가 HTTP 트랜잭션을 처리하는 방식을 살펴봅니다. 그리고 발생할 수 있는 몇 가지 성능 문제를 살펴보고, NGINX의 이벤트 중심 모델이 어떻게 이러한 HTTP 서버를 위한 매우 효과적인 가속 프록시가 되는지 살펴봅니다. NGINX와 NGINX keepalive 를 사용하면 실제 성능을 로컬 벤치마크 수준으로 되돌릴 수 있습니다.
실험실에서 서버를 벤치마킹한 후 실제 트래픽에 배포했는데 벤치마크 성능에 근접하지 못한다는 사실을 알게 된 적이 있으신가요? CPU 사용률은 낮고 사용 가능한 리소스는 충분하지만, 클라이언트는 느린 응답 시간에 대해 불평하고 서버의 활용도를 높일 방법을 찾지 못합니다.
애플리케이션의 속도와 확장성을 개선하기 위해 Linux 및 NGINX를 튜닝하는 방법에 대한 지침은 블로그에서 NGINX 성능을 위해 최적화하기를 참조하세요.
목차
1. HTTP Keepalive 연결 소개
2. HTTP Keepalive 가 미치는 영향은 무엇입니까?
3. HTTP Keepalive 이것이 실제로 무엇을 의미합니까?
4. 벤치마크 테스트 중 이러한 효과가 나타나지 않는 이유는 무엇입니까?
5. HTTP Keepalive 문제가 얼마나 흔한가요?
6. NGINX를 가속 HTTP 프록시로 사용
7. NGINX가 서비스를 가속화할 수 있는 다른 방법
8. 일반적인 로드 밸런서나 ADC가 아닙니다.
1. HTTP Keepalive 연결 소개
HTTP keepalive 연결은 지연 시간을 줄이고 웹 페이지를 더 빠르게 로드할 수 있도록 하는 필수 성능 기능입니다.
HTTP는 간단한 텍스트 기반 프로토콜입니다. 이전에 해본 적이 없다면 웹 브라우저에 있는 것과 같은 HTTP 디버깅 도구의 출력을 살펴보고 표준 요청 및 응답 구조를 확인해보세요.

가장 간단한 구현에서는 HTTP 클라이언트가 대상 서버에 새 TCP 연결을 생성하고 요청을 작성한 후 응답을 받습니다. 그런 다음 서버는 리소스를 해제하기 위해 TCP 연결을 닫습니다.

이 작동 모드는 특히 요소가 많은 복잡한 웹페이지나 네트워크 링크가 느린 경우 매우 비효율적일 수 있습니다. 새 TCP 연결을 만들려면 “three-way handshake”가 필요하고, 연결을 끊으려면 two-way shutdown도 필요합니다. 메시지마다 하나씩 TCP 연결을 반복적으로 만들고 닫는 것은 전화 통화에서 상대방이 말을 끝낸 후 전화를 끊고 다시 거는 것과 비슷합니다.
HTTP는 keepalive 연결이라는 메커니즘을 사용하여 HTTP 트랜잭션이 완료된 후에도 클라이언트와 서버 간의 TCP 연결을 열어둡니다. 클라이언트가 다른 HTTP 트랜잭션을 수행해야 하는 경우 새 TCP 연결을 만드는 대신 유휴 keepalive 연결을 사용할 수 있습니다.

클라이언트는 일반적으로 서버에 대한 여러 개의 동시 TCP 연결을 열고 모든 연결에서 keepalive 트랜잭션을 수행합니다. 이러한 연결은 일반적으로 유휴 시간 초과로 인해 클라이언트 또는 서버가 더 이상 필요하지 않다고 판단할 때까지 열려있습니다.
최신 웹 브라우저는 일반적으로 6~8개의 keepalive 연결을 열고 몇 분 동안 연결 상태를 유지한 후 시간이 초과됩니다. 웹 서버는 이러한 연결의 시간을 제한하고 더 빨리 닫도록 구성할 수 있습니다.
2. HTTP Keepalive 가 미치는 영향은 무엇입니까?
많은 클라이언트가 HTTP keepalive 를 사용하는 데 웹 서버에 동시성 제한이나 확장성 문제가 있는 경우, 해당 제한에 도달하면 성능이 급격히 저하됩니다.
위의 접근 방식은 개별 클라이언트에 최상의 성능을 제공하도록 설계되었습니다. 안타깝게도 “tragedy of commons“와 같은 시나리오에서 모든 클라이언트가 이러한 방식으로 작동하면 많은 일반 웹 서버 및 웹 애플리케이션의 성능에 해로운 영향을 미칠 수 있습니다.
그 이유는 많은 서버에 고정된 동시성 제한이 있기 때문입니다. 예를 들어, 일반적인 구성에서 Apache HTTP 서버는 제한된 수의 동시 TCP 연결만 처리할 수 있습니다: worker multiprocessing 모듈(MPM)을 사용하면 150개, prefork MPM을 사용하면 256개입니다. 각 유휴 HTTP keepalive 연결은 이러한 동시성 슬롯 중 하나를 사용하며, 모든 슬롯이 사용되면 서버는 더 이상 HTTP 연결을 수락할 수 없습니다.
일반적인 상식으로는 웹 서버에서 HTTP keepalive 를 끄거나 매우 짧은 수명으로 제한하는 것이 좋습니다. Keepalive는 SlowHTTPTest 및 Slowloris denial-of-service attack에 매우 간단한 벡터를 제공합니다(빠른 해결책은 serverfault.com에서 Keep-Dead 서비스 거부로부터 보호하기를 참조하세요).
또한 이러한 웹 및 애플리케이션 서버는 일반적으로 각 연결에 대해 운영체제 스레드 또는 프로세스를 할당합니다. TCP 연결은 매우 가벼운 운영체제 객체이지만 스레드 또는 프로세스는 매우 무겁습니다. 스레드와 프로세스는 메모리가 필요하고 운영체제에서 적극적으로 관리해야 하며 스레드 또는 프로세스 간의 “컨텍스트 전환”은 CPU를 소모합니다. 각 연결에 자체 스레드나 프로세스를 할당하는 것은 매우 비효율적입니다.

많은 수의 동시 클라이언트 연결과 각 연결에 스레드 또는 프로세스가 할당되면 가벼운 HTTP 트랜잭션을 처리하는 데 불균형적으로 많은 노력이 필요한 “HTTP heavy lifting”이라는 현상이 발생합니다.
3. HTTP keepalive 이것이 실제로 무엇을 의미합니까?
최신 웹 및 애플리케이션 서버에서는 많은 클라이언트가 동시성 제한을 소진하는 데 많은 시간이 걸리지 않습니다.
클라이언트가 8개의 TCP 연결을 열고 마지막 사용 후 각 연결을 15초 동안 유지하면 클라이언트는 15초 동안 8개의 동시성 슬롯을 소비합니다. 클라이언트가 초당 1명의 비율로 웹사이트에 도착하는 경우 120개의 동시성 슬롯이 유휴 keepalive 연결에 의해 지속적으로 점유됩니다. 초당 클라이언트 수가 2명인 경우 240개의 동시 접속 슬롯이 사용됩니다. 슬롯이 모두 소진되면 기존 연결이 시간 초과될 때까지 새 클라이언트가 연결할 수 없습니다.
이로 인해 서비스 수준이 매우 불균일해질 수 있습니다. Keepalive 연결을 성공적으로 획득한 클라이언트는 마음대로 서비스를 탐색할 수 있습니다. 동시 접속 슬롯이 모두 사용되었을 때 연결을 시도하는 클라이언트는 접속이 차단되어 대기열에서 기다려야 합니다.
4. 벤치마크 테스트 중 이러한 효과가 나타나지 않는 이유는 무엇입니까?
이러한 문제는 많은 클라이언트가 있는 느린 네트워크에서만 나타납니다. 빠른 로컬 네트워크에서 단일 클라이언트로 벤치마킹할 때는 이러한 문제가 나타나지 않습니다.
벤치마크에서 이러한 효과를 볼 수 없는 데에는 몇 가지 이유가 있습니다.
- 벤치마크 중에 keepalive를 활성화하지 않으면 클라이언트는 각 트랜잭션에 대해 새 TCP 연결을 생성합니다(트랜잭션이 완료되면 연결이 끊어짐). 빠른 로컬 네트워크에서 벤치마크를 실행할 가능성이 높기 때문에 벤치마크가 성공하고 keepalive를 사용하지 않을 때 발생하는 성능 문제가 나타나지 않습니다.
- keepalive를 활성화하면 서버의 제한보다 적은 수의 동시 연결을 실행할 수 있으며 벤치마크 클라이언트가 각 연결을 포화시켜(반복적으로 사용) 서버를 최대 용량까지 끌어올릴 가능성이 높습니다. 그러나 이는 실제 연결 프로필과 유사하지 않습니다.
대부분의 벤치마크 도구는 성공적인 트랜잭션에 대해서만 보고한다는 점에 유의하세요. 리소스 고갈로 인해 중단된 연결은 보고되지 않거나 성공한 연결의 극히 일부에 불과한 것처럼 보일 수 있습니다. 이는 실제 트래픽 문제의 본질을 숨길 수 있습니다.
5. HTTP keepalive 문제가 얼마나 흔한가요?
모든 스레드 또는 프로세스 기반 웹 또는 애플리케이션 서버는 동시성 제한에 취약합니다.
이 문제는 각 연결에 스레드 또는 프로세스를 할당하는 모든 웹 또는 애플리케이션 플랫폼에 내재되어 있습니다. 최적화된 벤치마크 환경에서는 발견하기 쉽지 않지만, 실제 환경에서는 성능 저하와 과도한 CPU 사용률로 나타납니다.
이 문제를 해결하기 위해 취할 수 있는 몇 가지 조치가 있습니다:
- 스레드 또는 프로세스 수 늘리기 – 이는 매우 단기적인 조치입니다. 스레드와 프로세스는 무거운 운영체제 객체이므로 점점 더 많이 생성될수록 관리 오버헤드가 급격히 증가합니다.
- HTTP keepalive 사용을 비활성화하거나 제한 – 동시성 제한을 연기할 수 있지만 각 클라이언트 성능이 훨씬 저하됩니다.
- 특수 keepalive 처리 사용 – Apache HTTP 서버(웹 서버)에는 작업자 스레드와 전용 이벤트 스레드 간에 연결이 “active” 상태와 “idle keepalive” 상태 사이를 이동할 때 연결을 이동하는 비교적 새로운 이벤트 MPM이 있습니다. 사용하는 다른 모듈이 이 MPM을 지원하는 경우 이 옵션을 사용할 수 있지만, SSL/TLs 연결은 여전히 전용 스레드에서 전적으로 처리된다는 점에 유의하세요.
- 보다 효율적인 처리 모델 사용 – 가장 간단하고 효과적인 방법은 웹 또는 애플리케이션 서버 앞에 효율적인 HTTP 프록시를 배치하는 것입니다. NGINX와 같은 이벤트 기반 프록시에는 위에서 설명한 동시성 제한이 없습니다. 느린 연결과 유휴 keepalive에도 웃으며 대처할 수 있습니다. 또한 여러 개의 유휴 keepalive 연결이 있는 느린 클라이언트 측 연결을 웹 및 애플리케이션 서버에서 가능한 최상의 성능을 추출하는 빠르고 로컬이며 매우 효율적인 벤치마크 스타일 연결로 효과적으로 변환합니다.
6. NGINX를 가속 HTTP 프록시로 사용
NGINX는 위에서 설명한 동시성 문제를 겪지 않는 다른 아키텍처를 사용합니다. 느린 클라이언트 연결을 벤치마크에 최적화된 연결로 변환하여 서버에서 최상의 성능을 끌어냅니다.
NGINX는 매우 효율적인 이벤트 중심 모델을 사용하여 연결을 관리합니다.
각 NGINX 프로세스는 동시에 여러 연결을 처리할 수 있습니다. 새 연결이 수락되면 위에서 설명한 프로세스별 또는 스레드별 모델과 달리 새 file descriptor와 폴링할 새 이벤트로 구성되는 오버헤드가 매우 낮습니다. NGINX에는 매우 효과적인 이벤트 루프가 있습니다:

이를 통해 각 NGINX 프로세스는 동시에 수만, 수천 또는 수십만 개의 연결로 쉽게 확장할 수 있습니다.
그런 다음 NGINX는 로컬 keepalive 연결 풀을 사용하여 요청을 업스트림 서버로 프록시합니다. TCP 연결을 열고 닫을 때 오버헤드가 발생하지 않으며, TCP 스택이 최적의 창 크기와 재시도 매개변수에 빠르게 적응합니다. 최적화된 로컬 네트워크를 통해 요청을 작성하고 응답을 읽는 속도가 훨씬 빨라집니다.

그 결과 업스트림 서버는 빠른 네트워크를 통해 단일 로컬 클라이언트(NGINX)와 통신하게 되고, 이 클라이언트는 HTTP keepalive 연결을 최적으로 활용하여 불필요하게 연결을 열어두지 않고 연결 설정을 최소화할 수 있습니다. 이를 통해 서버를 최적의 벤치마크 환경으로 되돌릴 수 있습니다.
NGINX가 HTTP 프록시 역할을 하는 것을 볼 수 있습니다:
- 기존 리소스의 활용도 향상. 웹 및 애플리케이션 서버는 더 이상 HTTP 작업을 많이 수행하지 않으므로 초당 더 많은 트랜잭션을 처리할 수 있습니다.
- 오류율 감소. NGINX가 모든 클라이언트에 대한 중앙 스케줄러 역할을 하기 때문에 HTTP 시간 초과가 훨씬 줄어듭니다.
- 최종 사용자 성능 향상. 서버가 더 효율적으로 실행되고 서비스 연결이 더 빨라집니다.
7. NGINX가 서비스를 가속화할 수 있는 다른 방법
HTTP 과부하 부담을 제거하는 것은 과부하가 걸린 애플리케이션 인프라에 NGINX가 제공할 수 있는 성능 혁신 조치 중 하나에 불과합니다.
NGINX의 HTTP-caching 기능은 표준 캐시 시맨틱에 따라 업스트림 서버의 응답을 캐시하여 캐시되는 항목과 기간을 제어할 수 있습니다. 여러 클라이언트가 동일한 리소스를 요청하는 경우 NGINX는 캐시에서 응답할 수 있으며, 중복 요청으로 업스트림 서버에 부담을 주지 않습니다.
또한 NGINX는 업스트림 서버에서 다른 작업을 offload 할 수 있습니다. 대역폭 사용량을 줄이기 위해 데이터 압축 작업을 offload하고, SSL/TLS 암호화 및 복호화를 중앙 집중화하고, 초기 클라이언트 인증(예: HTTP 기본 인증, Sub-request to external authentication server, JSON 웹 토큰)을 수행하고, 필요한 경우 모든 종류의 규칙을 적용하여 트래픽을 제한할 수 있습니다.
8. 일반적인 로드 밸런서나 ADC가 아닙니다.
마지막으로, 다른 가속 프록시, 로드 밸런서 또는 애플리케이션 전송 컨트롤러(ADC)와 달리 NGINX는 완전한 웹 서버라는 점도 잊지 마세요. NGINX를 사용하여 정적 콘텐츠를 제공하고, Java, PHP, Python, Ruby 및 기타 언어용 애플리케이션 서버로 트래픽을 배포하고, 미디어(오디오 및 비디오)를 전송하고, 인증 및 보안 시스템과 통합하고, NGINX 구성에 내장된 규칙을 사용하여 트랜잭션에 직접 응답할 수도 있습니다.
내장된 성능 제한이 없기 때문에 NGINX 및 NGINX Plus는 현재와 미래에 배포하는 하드웨어를 최대한 활용할 수 있습니다.
지금 바로 NGINX Plus 30일 무료 체험판을 시작하거나 NGINX STORE에 문의하여 사용 사례에 대해 논의하세요.
NGINX에 대한 최신 정보들을 빠르게 전달받고 싶으시다면, 아래의 뉴스레터를 구독하세요.
댓글을 달려면 로그인해야 합니다.