[4장] 커넥션 관리

DAYEON·2021년 8월 19일
0

HTTP 완벽 가이드

목록 보기
2/8
post-thumbnail

4.1 TCP 커넥션

  • 전 세계 모든 HTTP 통신은 TCP/IP를 통해 이루어짐
  • 세계 어디서든 클라이언트 애플리케이션은 서버 애플리케이션으로 TCP/IP와 커넥션을 맺을 수 있음.
  • 커넥션이 맺어지면 클라이언트와 서버 컴퓨터 간에 주고받은 메시지들은 보전되며 안전하게 전달됨.

커넥션 7단계
(1) 브라우저가 호스트 명 추출
(2) 브라우저가 호스트 명에 대한 IP 주소 찾음
(3) 브라우저가 포트 번호를 얻음
(4) 브라우저가 해당 포트로 TCP 커넥션을 생성
(5) 브라우저가 서버로 HTTP GET 요청 메시지를 보냄
(6) 브라우저가 서버에서 온 HTTP 응답 메시지를 읽음
(7) 브라우저가 커넥션을 끊음


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

  • TCP 커넥션의 한쪽에 있는 바이트들은 반대쪽으로 순서에 맞게 정확히 전달됨

4.1.2 스트림은 나뉘어 IP 패킷을 통해 전송된다.

  • TCP는 IP 패킷(IP 데이터그램)이라는 작은 조각을 통해 데이터를 전송
  • HTTP는 프로토콜 스택에서 최상위 계층
  • HTTP + 보안 기능 = HTTPS = TLS = SSL = HTTP와 TCP 사이에 있는 암호화 계층
  • TCP는 세그먼트 단위로 스트림을 잘게 나눠 IP 패킷으로 감싸 보냄
  • TCP/IP 소프트웨어 의해 처리됨

IP 패킷 구성 요소

  • IP 패킷 헤더
  • TCP 세그먼트 헤더
  • TCP 데이터 조각

4.1.3 TCP 커넥션 유지하기

  • 컴퓨터는 항상 TCP를 여러 개 가지고 있음
  • TCP는 포트 번호를 통해 이런 여러 개의 커넥션을 유지
  • 발신지 IP 주소, 발신지 포트, 수신지 IP 주소, 수신지 포트 값으로 유일한 커넥션을 생성

4.1.4 TCP 소켓 프로그래밍

  • 운영체제는 TCP 커넥션의 생성과 관련된 여러 기능 제공
  • 소켓 API 사용 시, TCP 종단 데이터 구조를 생성하고, 원격 서버의 TCP 종단에 그 종단 데이터 구조를 연결하여 테이터 스트림 읽기/쓰기 가능
  • TCP IP는 핸드셰이킹, 데이터 스트림과 IP 패킷 간의 분할 및 재조립에 대한 모든 세부사항을 외부로부터 숨김

🔎 핸드셰이킹 : 주고받기. 두 개의 통신 채널의 변수를 동적으로 설정하는 자동화된 협상 과정



4.2 TCP의 성능에 대한 고려

  • HTTP는 TCP 바로 위에 있는 개층이기 때문에 HTTP 성능은 그 아래 계층, TCP 성능에 영향을 받음

4.2.1 HTTP 트랜잭션 지연

  • HTTP 지연은 대부분 TCP 네트워크 지연때문에 발생
  • HTTP 트랜잭션 지연 원인
    • URI에 기술되어 있는 호스트에 방문한 적이 최근에 없는 경우 (호스트명 → IP 주소)
    • 수백 개의 HTTP 트랜잭션이 존재할 때, 새로운 TCP 커넥션으로 인해 커넥션 설정 시간이 발생하는 경우
    • 요청 메시지가 인터넷을 통해 전달되고 서버에 의해서 처리되어 시간이 소요되는 경우
    • 웹 서버가 HTTP 응답을 보내는 경우

4.2.2 성능 관련 중요 요소

(생략)

4.2.3 TCP 커넥션 핸드셰이크 지연

TCP 커넥션이 핸드셰이크를 하는 순서

  • (1) 커넥션 생성 요청 :
    클라이언트가 새로운 TCP 커넥션을 생성하기 위해 작은 TCP를 서버에 보냄. 해당 패킷은 'SYN'라는 특별한 플래그 가짐.
  • (2) 커넥션 받음 :
    몇 가지 커넥션 매개변수를 산출, 'SYN'과 'ACK' 플래그를 포함한 TCP 패킷을 클라이언트에 보냄
  • (3) 확인응답 신호 :
    클라이언트는 커넥션이 잘 맺어졌음을 알리기 위해 서버에게 다시 확인응답 신호를 보냄. 오늘날 TCP는 데이터까지 함께 보낼 수 있음.
  • 패킷들은 보이지 않게 TCP 소프트웨어가 관리 (HTTP 프로그래머들은 확인 불가능)
  • HTTP 트랜잭션은 TCP를 구성하는 것에 많은 시간을 할애.
  • → 이러한 지연을 제거하기 위해 이미 존재하는 커넥션을 재활용

4.2.4 확인응답 지연

  • 인터넷 라우터는 과부화가 걸렸을 시, 패킷을 마음대로 파기할 수 있음 (패킷 전송 보장 ✕)
  • TCP는 성공적인 데이터 전송을 보장하기 위해 자체적인 확인 체계를 가짐 → "확인 응답"
    • 무결성 체크섬 :
      각 세그먼트의 수신자는 세그먼트를 온전히 받으면 작은 확인응답 패킷을 송신자에게 반환
    • 편승(piggyback) :
      TCP는 송출 데이터 패킷과 확인응답을 하나로 묶음으로써 네트워크를 좀 더 효율적으로 사용

4.2.5 TCP 느린 시작(slow start)

  • TCP 커넥션은 시간이 지나면서 자체적으로 튜닝됨
  • 처음에는 커넥션의 최대 속도를 제한하고 데이터가 성공적으로 전송됨에 따라 속도 제한 높임
  • 급작스러운 부하와 혼잡을 방지
  • TCP가 한 번에 전송할 수 있는 패킷의 수 제한
  • 혼잡 윈도를 연다 (확인 응답을 받으면 2개의 패킷을 보낼 수 있는 방식)

4.2.6 네이글(Nagle) 알고리즘과 TCP _NODELAY

  • TCP는 데이터 스트림 인터페이스를 제공
  • 세그먼트가 최대 크기가 되지 않으면 전송을 하지 않음
  • 모든 패킷이 확인응답을 받았을 경우 최대 크기보다 작은 패킷의 전송을 허락
  • HTTP 성능에 관련해 여러 문제를 발생시킴
    • 크기가 작은 HTTP 메시지는 패킷을 채우지 못하기 때문에, 예측 불가능한 추가적인 데이터를 기다리며 지연됨
    • 확인응답 지연과 함께 쓰일 경우 형편없이 동작 (지연에 지연을 더함)
  • HTTP 애플리케이션은 성능 향상을 위해 HTTP 스택에 TCP _NODELAY 파라미터 값을 설정하여 네이글 알고리즘을 비활성화하기도 함 → 큰 크기의 데이터 덩어리 만들어야 함

4.2.7 TIME_WAIT의 누적과 포트 고갈

  • TIME_WAIT 포트 고갈은 성능 측정 시에 심각한 성능 저하를 발생시키지만 보통 실제 상황에서는 문제를 발생시키지 않음
  • TCP 커넥션의 종단에서 TCP를 끊으면, 종단에서는 커넥션의 IP 주소와 포트 번호를 메모리의 작은 제어영역에 기록 (일정 시간동안 TCP 커넥션 생성 방지 목적)
  • 현대의 빠른 라우터들 적용 이후, 커넥션 닫힌 후 패킷이 중복되는 경우 거의 사라짐
  • 주의할 점
    • 이전 커넥션의 패킷이 그 해당 커넥션과 같은 연결 값으로 생성되면 패킷이 중복되며 TCP 데이터 충돌
    • 포트 고갈 문제를 겪지 않더라도, 많은 커넥션 맺기나 많은 대기 상태의 제어 블록이 있는 상황은 주의


4.3 HTTP 커넥션 관리

  • (생략)

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

  • HTTP는 중개 서버가 놓이는 것을 허락
  • HTTP 메시지는 중개 서버들을 하나하나 거치면서 전달됨

Connection 헤더에 전달될 수 있는 3가지 종류의 토큰

  • HTTP 헤더 필드명은 해당 커넥션에만 해당되는 헤더들 나열
  • 임시적인 토큰 값 = 커넥션에 대한 비표준 옵션
  • close 값 = 커넥션 작업 완료 시 종료되어야 함을 의미
  • Connection 헤더에 있는 모든 헤더 필드들은 메시지를 다른 곳을 전달하는 시점에 삭제
  • 헤더 보호하기 (Connection 헤더에서 흡별 헤더 명을 기술하는 것)

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

  • 커넥션 관리는 TCP 성능에 영향을 미침
  • 순차적 처리의 단점
    • 순차적인 처리로 인한 지연에는 물리적인 지연뿐이 아닌, 심리적인 지연 또한 존재
    • 특정 브라우저의 경우, 모든 객체를 내려받기 전까지 빈화면 보여줌 (크기 측정 때문)
  • HTTP 커넥션 성능 향상을 위한 최신 기술
    • 병렬 커넥션 : 여러 개의 TCP 커넥션을 통한 동시 HTTP 요청
    • 지속 커넥션 : 커넥션을 맺고 끊는 데서 발생하는 지연을 제거하기 위한 TCP 커넥션 재활용
    • 파이프라인 커넥션 : 공유 TCP 커넥션을 통한 병렬 HTTP 요청
    • 다중 커넥션 : 요청과 응답들에 대한 중재(실험적인 기술)


4.4 병렬 커넥션

  • (생략)

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

  • 하나의 커넥션으로 단일 커넥션의 대역폭 제한과 커넥션이 동작하지 않고 있는 시간 활용
  • 각 커넥션의 지연 시간을 겹치게 함
  • 나머지 객체를 내려받는 데에 남은 대역폭 사용

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

  • 클라이언트의 네트워크 대역폭이 좁을 때, 각 객체를 전송받는 것은 느리기 때문에 성능상의 장점은 거의 없어짐
  • 다수의 커넥션은 메모리를 많이 소모하고 자체적인 성능 문제를 발생
  • 브라우저는 실제로 병렬 커넥션을 사용하긴 하지만 적은 수의 병렬 커넥션만을 허용

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

  • 화면 여러 개의 객체가 동시에 보이면서 내려받고 있는 상황을 볼 수 있기 때문에 사용자는 빠르다고 느낄 수 있음


4.5 지속 커넥션

  • 처리가 완료된 후에도 TCP 커넥션을 유지하여 앞으로 있을 HTTP 요청에 재사용 가능
  • TCP의 느린 시작으로 인한 지연 피함

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

  • 병렬 커넥션의 단점
    • 각 트랜잭션마다 새로운 커넥션을 맺고 끊음으로 인해 시간과 대역폭이 소요
    • 각각의 새로운 커넥션은 TCP 느린 시작으로 인해 성능 저하
    • 실제로 연결할 수 있는 병렬 커넥션의 수 제한
  • 지속 커넥션의 장점
    • 커넥션 맺기 전 사전 작업과 지연 줄이며 튜닝된 커넥션 유지
    • 커넥션의 수 줄임
  • 지속 커넥션의 단점
    • 지속 커넥션을 잘못 관리할 경우 연결 상태의 수많은 커넥션이 누적 (불필요한 소모 발생)
  • → 지속 커넥션은 병렬 커넥션과 함께 사용할 때 가장 효과적

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

  • 연속적으로 각 커넥션을 생성하여 처리하는 방식과 비교했을 때, 지속 커넥션으로만 처리하는 방식은 시간을 단축시킴

4.5.3 Keep-Alive 동작

  • 사용하지 않기로 결정되어 HTTP/1.1 명세에서 제외
  • 하지만 아직 널리 사용.

4.5.4 Keep-Alive 옵션

  • Keep-Alive 헤더를 커넥션을 유지하기 바라는 요청일 뿐 (무조건 따를 필요 없음)
  • 동작은 헤더의 쉼표로 구분된 옵션들로 제어 가능

제어 가능한 옵션들

  • timeout
  • max 파라미터
  • 헤더에서 임의의 속성 (문법 → 이름[=값])

4.5.5 Keep-Alive 커넥션 제한과 규칙

  • 클라이언트는 Keep-Alive 커넥션을 사용하기 위해 Connection:Keep-Alive 요청 헤더를 보내야 함
  • 커넥션이 끊어지기 전, 엔터티 본문의 길이를 알아햐 유지 가능
  • 프락시와 게이트웨이는 메시지를 전달하거나 캐시에 넣기 전 Connection 헤더에 명시된 모든 헤더 필드와 Connection 헤더를 제거해야 함
  • Keep-Alive 커넥션은 Connection 헤더를 인식하지 못하는 프락시 서버와는 맺어지면 안됨
  • 클라이언트는 응답 전체를 모두 받기 전 커넥션이 끊어졌을 경우, 요청을 다시 보낼 수 있도록 준비해야 함

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

  • Connection 헤더의 무조건 전달
    • 프락시는 Connection 헤더를 이해하지 못해서 해당 헤더들을 삭제하지 않고 요청 그대로를 다음 프락시에 전달
    • 통신에 혼선이 생겨 브라우저는 자신이나 서버가 타임아웃이 나서 커넥션이 끊길 때까지 기다림
  • 프락시와 흡별 헤더
    • 프락시는 Connection 헤더와 헤더에 명시된 헤더들을 절대 전달하면 안됨
    • 헤더 뿐만 아니라 Keep-Alive 이름의 헤더도 전달하면 안됨

4.5.7 Proxy-Connection 살펴보기

  • 클라이언트의 요청이 중개서버를 통해 이어지는 경우 모든 헤더를 무조건 전달하는 문제를 해결하기 위해 사용
  • Connection 헤더 대신 비표준인 Proxy-Connection 확장 헤더를 프락시에게 전달
  • 영리한 프락시는 Proxy-Connection 헤더가 Keep-Alive를 요청하는 것임을 인식하여 자체적으로 Connection:Keep-Alive 헤더를 웹 서버에 전송
  • 이 방식은 클라이언트와 서버 사이 한 개의 프락시만 있는 경우에서만 동작

4.5.8 HTTP/1.1 지속 커넥션

  • 애플리케이션은 트랜잭션이 끝난 다음 커넥션을 끊으려면 Connection:close 헤더를 명시해야 함
  • 위를 보내지 않는다고 하여 서버가 커넥션을 영원히 유지하겠다는 것을 뜻하진 않음

4.5.9 지속 커넥션의 제한과 규칙

상세 내용

  • 클라이언트가 요청에 Connection:close 헤더를 포함했으면, 클라이언트는 해당 커넥션으로 추가적인 요청 보낼 수 없음
  • 클라이언트가 해당 커넥션으로 추가 요청을 보내지 않을 것이라면, Connectoin:close 명시
  • 커넥션에 있는 모든 메시지가 자신의 길이 정보를 가지고 있을 때만 커넥션 지속 가능
  • 하나의 사용자 클라이언트는 서버의 과부하 방지를 위해 넉넉잡아 두 개의 지속 커넥션만을 유지 (N명 → 2N개 커넥션 유지)


4.6 파이프라인 커넥션

파이프라인 제약 사항

  • HTTP 클라이언트는 커넥션이 지속 커넥션인지 확인하기 전까지는 파이프라인을 이어서는 안됨
  • HTTP 응답은 요청 순서와 같게 와야 함
  • HTTP 클라이언트는 커넥션이 언제 끊어지더라도, 완료되지 않은 요청이 파이프라인에 있으면 언제든 다시 요청을 보낼 준비가 되어 있어야 함
  • HTTP 클라이언트는 POST 요청같이 반복해서 보낼 경우 문제가 생기는 요청은 파이프라인을 통해 보내면 안됨


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

  • 커넥션 관리에는 명확한 기준이 없음.

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

  • 어떠한 HTTP 클라이언트, 서버, 혹은 프락시든 언제든지 TCP 전송 커넥션을 끊을 수 있음
  • 단, 그 끊는 시점에 데이터를 전송하지 않을 것이라고 확신하지 못하기 때문에, 클라이언트는 요청 메시지를 보내는 도중에 문제가 생길 수 있음

4.7.2 Content-Length와 Truncation

  • 각 HTTP 응답은 본문의 정확한 크기 값을 가지는 Content-Length 헤더를 가지고 있어야 함
  • 클라이언트나 프락시가 커넥션이 끊어졌다는 HTTP 응답을 받은 후, 이에 대한 값이 일치하지 않거나 존재하지 않는다면 수신자는 정확한 길이를 서버에 물어야 함

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

  • 커넥션은 에러가 없더라도 언제든 끊기 가능
  • 이로 인한 부작용 주의
  • 멱등(실행 횟수에 상관 없이 같은 결과를 반환함)이 아닌 요청은 파이프라인을 통해 요청하면 안됨
  • 멱등 메서드들 GET, HEAD, PUT, DELETE, TRACE
  • 비멱등인 메서드나 순서에 대해 에이전트가 요청을 다시 보낼 수 있도록 기능을 제공한다 하더라도, 자동을 재시도하면 안됨

4.7.4 우아한 커넥션 끊기

  • 전체 끊기와 절반 끊기
    • close()를 호출하면 커넥션의 입력/출력 채널 커넥션 모두 끊음
    • shutdown()을 호출하면 입력/출력 채널 중 하나를 개별적으로 끊음
  • TCP 끊기와 리셋 에러
    • 애플리케이션이 각기 다른 HTTP 클라이언트, 서버, 프락시와 통신할 때, 파이프 라인 지속 커넥션을 사용할 땐 절반 끊기를 사용해야 함
    • 보통은 커넥션의 출력 채널을 끊는 것이 안전
  • 우아하게 커넥션 끊기
  • 애플리케이션 자신의 출력 채널을 먼저 끊고 다른 쪽에 있는 기기의 출력 채널이 끊기는 것을 기다리는 것
  • 끊기 후에도 주기적으로 상태 검사를 해야 함


profile
노력하는 초보 개발자

0개의 댓글