[HTTP] 커넥션 관리

Jade·2021년 3월 23일
0

4.1 TCP 커넥션

  • 전 세계 모든 HTTP 통신은 TCP/IP를 통해 이루어진다.
  • 클라이언트 애플리케이션은 서버 애플리케이션과 TCP/IP 커넥션을 맺고, 커넥션이 맺어지면 서로 간에 주고 받는 메시지들은 손실 혹은 손상되거나 순서가 바뀌지 않고 전달된다.

4.1.1 신뢰할 수 있는 데이터 전송 통로인 TCP

  • HTTP 커넥션은 몇몇 사용 규칙을 제외하고는 TCP 커넥션에 불과하다.
  • TCP 커넥션은 인터넷을 안정적으로 연결해준다.

4.1.2 TCP 스트림은 세그먼트로 나뉘어 IP 패킷을 통해 전송된다.

  • TCP는 세그먼트라는 단위로 데이터 스트림을 잘게 나누고, 세그먼트를 IP 패킷이라고 부르는 봉투에 담아 인터넷을 통해 데이터를 전달한다.
  • 이 모든 과정은 TCP/IP 소프트웨어에 의해 처리되며, 프로그래머에게 보이지 않는다.

4.1.3 TCP 커넥션 유지하기

  • 컴퓨터는 항상 TCP 커넥션을 여러 개 가지고 있다.
  • TCP는 포트 번호를 통해서 이런 여러 개의 커넥션을 유지한다.
  • TCP 커넥션은 <발신지 IP 주소, 발신지 포트, 수신지 IP 주소, 수신자 포트>값으로 유일한 커넥션을 생성하며, 서로 다른 두 개의 TCP 커넥션은 네 가지 주소 구성요소의 값이 모두 값을 수 없다.

4.1.4 TCP 소켓 프로그래밍

  • 소켓 API를 통해 운영체제의 TCP 프로그래밍 인터페이스를 살펴볼 수 있다.
  • 소켓 API를 사용하면 TCP 종단 데이터구조를 생성하고, 원격 서버의 TCP 종단에 그 종단 데이터 구조를 연결하여 데이터 스트림을 읽고, 쓸 수 있다.
  • TCP API는 기본적인 네트워크 프로토콜의 핸드셰이킹, TCP 데이터 스트림과 IP 패킷 간의 분할 및 재조립에 대한 모든 세부사항을 외부로부터 숨긴다.
  • (S4) 웹 서버는 커넥션을 기다린다.
  • (C3) 클라이언트는 URL에서 IP주소와 포트 번호를 알아내고 서버에 TCP 커넥션을 생성하기 시작한다.
  • (C5) 커넥션이 맺어지면 클라이언트는 HTTP 요청을 보내고
  • (S6) 서버는 그 요청을 읽는다.
  • (S7) 서버가 요청 메시지를 다 받으면, 그 요청을 분석하여 클라이언트가 원하는 동작을 수행하고 클라이언트에게 데이터를 보낸다.
  • (C6) 클라이언트는 그것을 받아
  • (C7) 응답 데이터를 처리한다.

4.2 TCP 성능에 대한 고려

  • HTTP는 TCP 바로 위에 있는 계층이기 때문에 TCP 계층 성능에 영향을 받는다.

4.2.1 HTTP 트랜잭션 지연

  • 클라이언트나 서버가 너무 많은 데이터를 내려받거나 복잡하고 동적인 자원들을 실행하지 않는 한, 대부분의 HTTP 지연은 TCP 네트워크 지연때문에 발생한다.
  • TCP 네트워크 지연은 하드웨어의 성능, 네트워크와 서버의 전송 속도, 요청과 응답 메시지의 크기, 클라이언트와 서버 간의 거리에 따라 크게 달라진다.

4.3 HTTP 커넥션 관리

4.3.1 흔히 잘못 이해하는 Connection 헤더

  • 어떤 경우에는 현재 맺고 있는 커넥션에만 적용될 옵션을 지정해야 할 때가 있다. 그 때 사용하는 것이 HTTP Connection 헤더 필드다. 이것은 커넥션 토큰을 쉼표로 구분하여 가지고 있으며, 그 값들은 다른 커넥션에 전달되지 않는다.

4.3.2 순차적인 트랜잭션 처리에 의한 지연

  • 순차적인 처리로 인한 지연에는 물리적인 지연뿐 아니라, 하나의 이미지를 내려받고 있는 중에는 웹페이지의 나머지 공간에 아무런 변화가 없어서 느껴지는 심리적인 지연도 있다.
  • 순차적 방식의 또 하나의 단점은, 특정 브라우저의 경우 객체를 화면에 배치하려면 객체의 크기를 알아야 하기 때문에, 모든 객체를 내려받기 전까지는 텅 빈 화면을 보여준다는 것이다.
  • 이러한 HTTP 성능을 향상시킬 수 있는 여러가지 최신 기술은 다음과 같다.
    • 병렬 커넥션: 여러 개의 TCP 커넥션을 통한 동시 HTTP 요청
    • 지속 커넥션: 커넥션을 맺고 끊는 데서 발생하는 지연을 제거하기 위한 TCP 커넥션의 재활용
    • 파이프라인 커넥션: 공유 TCP 커넥션을 통한 병렬 HTTP 요청
    • 다중 커넥션: 요청과 응답들에 대한 중재

4.4 병렬 커넥션

4.4.1 병렬 커넥션은 페이지를 더 빠르게 내려받는다.

  • 단일 커넥션의 대역폭 제한과 커넥션이 동작하지 않고 있는 시간을 활용하면, 객체가 여러 개 있는 웹페이지를 더 빠르게 내려받을 수 있을 것이다.

4.4.2 병렬 커넥션이 항상 더 빠르지는 않다.

  • 일반적으로 병렬 커넥션이 더 빠르기는 하지만, 항상 그렇지는 않다.
  • 클라이언트의 네트워크 대역폭이 좁다면, 제한된 대역폭 내에서 각 객체를 전송받는 것은 느리기 때문에 장점이 거의 없어진다.
  • 또한 다수의 커넥션은 메모리를 많이 소모하고 자체적인 성능 문제를 발생시킨다.
  • 브라우저는 실제로 병렬 커넥션을 사용하긴 하지만 적은 수(대부분 4개)의 병렬 커넥션만을 허용한다.

4.4.3 병렬 커넥션은 더 빠르게 '느껴질 수' 있다.

  • 병렬 커넥션이 실제로 페이지를 더 빠르게 내려받는 것은 아니지만, 화면에 여러 개의 객체가 동시에 보이면서 내려받고 있는 상황을 볼 수 있기 때문에 사용자는 더 빠르게 내려받고 있는 것처럼 느낄 수 있다.

4.5 지속 커넥션

  • HTTP/1.1을 지원하는 기기는 처리가 완료된 후에도 TCP 커넥션을 유지하여 앞으로 있을 HTTP 요청에 재사용 할 수 있다.
  • 처리가 완료된 후에도 계속 연결된 상태로 있는 TCP 커넥션을 지속 커넥션이라고 부른다.
  • 해당 서버에 맺어져 있는 지속 커넥션을 재사용함으로써, 커넥션을 맺기 위한 준비작업에 따르는 시간을 절약할 수 있고, TCP의 느린 시작으로 인한 지연을 피함으로써 더 빠르게 데이터를 전송할 수 있다.

4.5.1 지속 커넥션 vs 병렬 커넥션

  • 앞서 본 병렬 커넥션은 여러 객체가 있는 페이지를 더 빠르게 전송하지만, 다음과 같은 단점이 있다.
    • 각 트랜잭션마다 새로운 커넥션을 맺고 끊기 때문에 시간과 대역폭이 소요된다.
    • 각각의 새로운 커넥션은 TCP 느린 시작 때문에 성능이 떨어진다.
    • 실제로 연결할 수 있는 병렬 커넥션의 수에는 제한이 있다.
  • 이에 반해 지속 커넥션은 커넥션을 맺기 위한 사전 작업과 지연을 줄여주고, 튜닝된 커넥션을 유지하며, 커넥션의 수를 줄여준다. 하지만 이를 잘못 관리할 경우, 연결된 상태의 수많은 커넥션이 쌓여 리소스에 불필요한 소모를 발생시킬 것이다.
  • 지속 커넥션은 병렬 커넥션과 함께 사용될 때가 가장 효과적이다. 오늘날 주로 사용하는 방법엔 두 가지 타입이 있는데, HTTP/1.0+에는 'keep-alive' 커넥션이, HTTP/1.1에는 '지속'커넥션이 있다.

4.5.2 HTTP/1.0+의 Keep-Alive 커넥션

  • keep-alive 커넥션의 성능상의 장점은, 커넥션을 맺고 끊는데 필요한 작업을 없애 시간을 단축시킬 수 있다.

4.5.3 Keep-Alive 동작

  • keep-alive는 사용하지 않기로 결정되어 HTTP/1.1 명세에서 빠졌으나, 아직도 브라우저와 서버 간에 keep-alive 핸드셰이크가 널리 사용되고 있기 때문에 HTTP 애플리케이션은 그것을 처리할 수 있게 개발해야 한다.
  • 동작 방식을 간단히 살펴보면, 클라이언트는 커넥션을 유지하기 위해 요청에 Connection:Keep-alive헤더를 포함시키고, 이 요청을 받은 서버는 그 다음 요청도 이 커넥션을 통해 받고자 한다면, 응답 메시지에 같은 헤더를 포함시켜 응답한다.

4.5.4 Keep-Alive 옵션

  • Keep-Alive 헤더는 커넥션을 유지하기를 바라는 요청일 뿐, 무조건 그것을 따를 필요는 없다.
  • 또한 동작은 Keep-Alive 헤더의 쉼표로 구분된 옵션들로도 제어할 수 있다.
    • timeout 파라미터: 커넥션이 얼마나 유지될 것인지 설정
    • max 파라미터: 커넥션이 몇 개의 HTTP 트랜잭션을 처리할 때까지 유지될 것인지 설정

4.5.6 Keep-Alive와 멍청한(dump) 프락시

  • 클라이언트와 서버 사이에 존재하는 프락시는 Connection 헤더를 이해하지 못해서 헤더들을 삭제하지 않고 요청 그대로를 그 다음 프락시에 전달한다.
  • 이런 종류의 잘못된 통신을 피하려면, 프락시는 Connection: Keep-alive 헤더를 받으면 Connection 헤더뿐만 아니라, Keep-alive라는 헤더도 전달하면 안된다.

4.5.7 Porxy-Connection 살펴보기

  • 위와 같은 문제를 해결하기 위해 넷스케이프의 브라우저 및 프락시 개발자들은 다음과 같은 차선책을 제시하였다.
    • 클라이언트의 요청이 중개서버를 통해 이어지는 경우, Proxy-Connection이라는 헤더를 사용하는 것이다.

4.5.8 HTTP/1.1의 지속 커넥션

  • HTTP/1.1에서는 Keep-alive 커넥션을 지원하지 않는 대신, 설계가 더 개선된 지속 커넥션을 지원한다. 목적은 기존과 같지만 그에 비해 더 잘 동작한다.
  • 별도 설정을 하지 않는 한, 지속 커넥션은 기본으로 활성화 되어있다.

4.6 파이프라인 커넥션

  • HTTP/1.1에서는 지속 커넥션을 통해 요청을 파이프라이닝 할 수 있다. 이는 Keep-alive 커넥션의 성능을 더 높여준다.

4.7 커넥션 끊기에 대한 미스터리

4.7.1 '마음대로' 커넥션 끊기

  • 어떠한 HTTP 클라이언트, 서버, 혹은 프락시든 언제든지 TCP 전송 커넥션을 끊을 수 있다. 메시지가 전송 중이더라도 에러가 있는 상황에서는 헤더의 중간이나 다른 엉뚱한 곳에서 끊길 수 있다.

4.7.2 Content-Length와 Truncation

  • 각 HTTP 응답은 본문의 정확한 크기 값을 가지는 Conection-Length 헤더를 가지고 있어야 한다.
  • 클라이언트나 프락시가 커넥션이 끊어졌다는 HTTP 응답을 받은 후, 실제 전달된 엔터티의 길이와 Connection-Length의 값이 일치하지 않거나 자체가 존재하지 않으면 수신자는 데이터의 정확한 길이를 서버에게 물어봐야 한다. 이때, 만약 수신자가 캐시 프락시일 경우 응답을 하면 안된다. 프락시는 Connection-Length를 정정하려 하지 말고 받은 메시지를 그대로 전달해야 한다.

4.7.3 커넥션 끊기의 허용, 재시도, 멱등성

  • 커넥션은 심지어 아무런 에러가 없더라도 언제든 끊을 수 있다. 그러므로 HTTP 애플리케이션은 예상치 못한 커넥션 끊김에 대응할 수 있는 준비가 되어 있어야 한다.
  • 한 번 혹은 여러 번 실행됐는지에 상관없이 같은 결과를 반환한다면 그 트랜잭션은 멱등하다고 한다. GET, HEAD, PUT, DELETE, TRACE 그리고 OPTIONS 메서드들이 그렇다.
  • 반면 POST와 같이 멱등이 아닌 것은 파이프라인을 통해 요청하면 안된다. 그렇지 않으면 전송 커넥션이 예상치 못하게 끊어져 버렸을 때, 알 수 없는 결과를 초래할 수 있다.

4.7.4 우아한 커넥션 끊기

  • TCP 커넥션은 그림에서 보듯 양방향이다.
  • 양쪽에는 데이터를 읽고 쓰기 위한 입력 큐와 출력 큐가 있다.
  • 한쪽 출력 큐에 있는 데이터는 다른 쪽의 입력 큐에 보내질 것이다.

전체 끊기와 절반 끊기

  • 애플리케이션은 TCP 입력 채널과 출력 채널 중 한 개만 끊거나 둘 다 끊을 수 있다.
    • close()를 호출하면 TCP 커넥션읜 입력 채널과 출력 채널의 커넥션을 모두 끊는다.
    • shutdown()을 호출하면 두 가지 채널 중 하나를 개별적으로 끊는다.

TCP 끊기와 리셋 에러

  • 단순한 HTTP 애플리케이션은 전체 끊기인 close()만을 사용할 수 있다.
  • 하지만 애플리케이션이 각기 다른 HTTP 클라이언트, 서버, 프락시와 통신할 때 기기들에 예상치 못한 쓰기 에러를 발생하기 위해 절반 끊기를 사용해야 한다.

우아하게 커넥션 끊기

  • 일반적으로 애플리케이션이 우아한 커넥션 끊기를 구현하는 것은 애플리케이션 자신의 출력 채널을 먼저 끊고 다른 쪽에 있는 기기의 출력 채널이 끊기는 것을 기다리는 것이다. 양쪽에서 더는 데이터를 전송하지 않을 것이라고 알려주면 커넥션은 리셋의 위험 없이 온전히 종료된다.
  • 출력 채널에 절반 끊기를 하고 난 후에도 커넥션이 정확히 끊어졌는지 확인하기 위해 데이터나 스트림의 끝을 식별하도록 입력 채널에 대해 주기적인 상태 검사를 해야 한다.

📌 데이빗 고울리, 브라이언 토티, 마조리 세이어, 세일루 레디, 안슈 아가왈 공저 이응준, 정상일 공역, 『HTTP 완벽 가이드: 웹은 어떻게 동작하는가』, 인사이트(2014)

profile
우당탕탕 좌충우돌 인프라 여행기

0개의 댓글