Connection(커넥션)

HTTP는 기본적으로 TCP/IP를 기반으로 동작하기 때문에, HTTP 트랜잭션은 TCP 커넥션 위에서 이루어진다. HTTP는 TCP/IP 위에서 동작하기 때문에 TCP/IP의 특성을 그대로 가지게 되며, TCP/IP는 아래의 이점을 제공한다.

  • 오류 없는 데이터 전송

  • 순서에 맞는 전달

  • 조각나지 않는 데이터 스트림

TCP/IP 덕분에 신뢰성 있는 HTTP 통신이 가능해졌으나, 신뢰를 보장하기 위해 통신을 위한 커넥션을 맺고 끊는 과정이 발생한다. HTTP 트랜잭션을 처리하는 과정에서 커넥션을 맺고 끊는 부분에서 지연이 발생하는데, 전체 지연 시간의 대부분을 차지하기 때문에 HTTP 성능은 커넥션에 의해 크게 좌우되며, 커넥션 관리는 HTTP 성능에 큰 영향을 미친다.

성능 관련 중요 요소

TCP가 HTTP 성능에 영향을 주는 가장 일반적인 요소는 아래와 같다.

  • TCP 커넥션의 핸드셰이크 설정

    • 어떤 데이터를 전송하든 새로운 TCP 커넥션을 맺기 위한 조건을 맞추기 위해 IP 패킷을 교환해 핸드셰이크를 수행해야 함

    • 이 과정이 데이터 전송에 걸리는 시간 중 상당 부분을 차지

  • TCP 느린 시작(slow start)

    • TCP의 전송 속도는 가변적으로 변하며, 데이터 전송이 시작되면 전송 속도가 느리게 시작되어 점차 증가하는데, 이를 느린 시작이라고 함

  • 데이터를 한 번에 전송하기 위한 네이글(Nagle) 알고리즘

    • 네이글 알고리즘은 네트워크 효율을 위해 TCP 세그먼트보다 작은 여러 개의 데이터를 하나의 TCP 세그먼트로 전송하기 위한 알고리즘

    • 하나의 TCP 세그먼트로 전송되기까지 데이터가 모이기를 기다리기 때문에 지연이 발생

  • TCP 편승(piggyback) 확인 응답(ACK)을 위한 확인 응답 지연 알고리즘

    • 인터넷이 패킷 전송을 완벽히 보장하지 않음

    • TCP는 데이터 전송을 보장하기 위해 확인을 하는 확인 응답 알고리즘을 사용하는데, 지연의 원인이 됨

HTTP 커넥션 관리

초기 HTTP 에서는 실제로 통신 한 번 할때마다 TCP에 의해 연결이 되고 종료됐기 때문에 각 트랜잭션마다 TCP 핸드쉐이크가 일어났기 때문에, TCP 커넥션을 맺고 끊는 과정이 필요하게 되어 비효율적인 부분을 해결하기 위해 여러 가지 방법들이 제시되어, 해당 문제는 완화되고 있다.

연결 방법연결 방식

개별 연결(Non-Persistent Connection)

연결 후 요청 후 응답 후 연결 종료

병렬 연결(Parallel Connection)

여러 개의 TCP 커넥션을 통한 동시 HTTP 요청

지속 연결(Persistent Connection)

커넥션을 맺고 끊는 데서 발생하는 지연을 제거하기 위해 TCP 커넥션 재활용

파이프라인 연결(Pipelined Connection)

공유 aTCP 커넥션을 통한 HTTP 요청

다중 연결(Multiplexing Connection)

요청과 응답들에 대한 중재(실험적 기술)

병렬 연결(Parallel Connection)

여러 개의 TCP 커넥션을 통해 동시에 HTTP 트랜잭션을 병렬로 처리할 수 있도록 하는 방식으로, 대역폭에 여유가 있는 한 더 빠르게 데이터를 주고 받을 수 있다. 하지만 다수의 커넥션이 메모리를 많이 소모하고 성능 저하 문제를 유발 시킬 수 있다.

지속 연결(Persistent Connection)

웹 사이트는 보통 같은 사이트에 여러 개의 커넥션을 맺게 되는데, 처리가 완료된 커넥션을 계속 유지하여 다음 요청을 처리할 때 재사용하는 방식이다. 이를 통해 TCP 핸드쉐이크를 줄일 수 있고, 커넥션을 맺고 끊는 데서 발생하는 지연을 제거할 수 있다.

병렬 vs 지속

병렬 커넥션은 여러 객체가 있는 페이지를 빠르게 전송하지만 몇 가지 단점이 존재한다.

  • 각 트랜잭션마다 새로운 커넥션 생성 및 종료

  • 각 커넥션마다 TCP 느린 시작(slow start) 문제 발생

  • 제한적인 실제 연결 가능한 커넥션 수

이에 반해 지속 커넥션은 위의 단점을 보완할 수 있어, 현대 웹 애플리케이션에서는 적은 수의 병렬 커넥션을 지속하여 함께 사용하는 것이 일반적이다.

Keep-Alive

HTTP/1.0에서 Connection: Keep-Alive 헤더를 통해 지속 커넥션을 사용할 수 있도록 지원하였다. 초기에 설계에 많은 문제가 있었지만 아직 많은 웹 서버와 브라우저가 해당 헤더를 지원하고 있기 때문에, HTTP 애플리케이션은 해당 헤더를 처리할 수 있게 개발을 해야 한다. (1.1에서 문제가 해결되고 Connection: Keep-Alive 헤더는 더 이상 사용되지 않는 걸로 결정되어 명세에서 제거됨)

HTTP/1.0 keep-alive 커넥션을 구현한 클라이언트는 커넥션 유지를 위해 Connection: Keep-Alive 헤더를 포함시키게 되는데, 서버는 응답 메시지에 헤더를 포함시키거나 포함시키지 않거나 두 가지 방법으로 응답할 수 있다.

  • Connection: Keep-Alive 헤더를 포함시켜 응답을 보낸 경우: 지속 커넥션을 계속 유지하는 것으로 판단

  • 헤더를 포함시키지 않은 경우: 클라이언트느 서버가 keep-alive 커넥션을 지원하지 않는다고 판단하여 커넥션을 끊을 것이라 추정

Keep-Alive 헤더는 커넥션을 유지하기 바라는 요청인 명세이기 때문에 요청 받은 값에 대해 따를 필요가 없으며, 언제든지 커넥션이 한 쪽에 의해 종료될 수 있다. 마찬가지로 Keep-Alive 관련 옵션이 존재하지만, 반드시 지켜야 되는 것은 아니기 때문에 보장받을 수 없다.

  • timeout: 커넥션을 유지 시킬 시간

  • max: 한 커넥션이 처리할 수 최대 HTTP 트랜잭션 수

  • 그 외 임의 속성

HTTP/1.1

HTTP/1.1에서는 Keep-Alive가 명세에서는 빠지게 되고 기본적으로 지속 커넥션이 활성화 된 상태로, 별도 설정이 없는 한 모든 커넥션을 지속 커넥션으로 유지한다. 트랜잭션이 끝나고 커넥션을 종료하고 싶다면 Connection: close 헤더를 통해 커넥션을 종료하도록 명시할 수 있다.

파이프라인 연결(Pipelined Connection)

HTTP/1.1 지속 커넥션을 통해 요청을 파이프라이닝 하는 방법으로, 여러 개의 요청을 응답이 도착하기 전에 보내는 방식이다. 대기 시간이 긴 네트워크 상황에서 네트워크 왕복으로 인한 시간을 줄여 성능을 높여주지만 아래와 같은 제약 사항이 있다.

  • HTTP 클라이언트는 커넥션이 지속 커넥션인지 확인 전엔 파이프라인 연결을 사용할 수 없다.

  • 메시지 자체에 순번이 없기 때문에 응답의 순서를 구분할 수 없기 때문에 요청 순서와 응답 순서가 같아야 한다.

  • HTTP 클라이언트는 커넥션이 언제 끊어지더라도, 완료되지 않은 남은 요청이 파이프라인에 있으면 다시 보낼 수 있어야 한다.

  • POST 요청과 같은 비멱등 요청을 보내면 문제가 발생할 수 있기 때문에 멱등 요청만 파이프라인에 보낼 수 있다.

참고자료

Last updated