Post

커넥션 관리

4. 커넥션 관리

4.1 TCP 커넥션

전 세계 모든 HTTP 통신은 TCP/IP를 통해 이루어진다

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

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

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

TCP는 IP패킷이라고 불리는 작은 조각을 통해 데이터를 전송

HTTP가 메시지를 전송하고자 할 경우, 현재 연결되어 있는 TCP 커넥션을 통해서 메시지 데이터의 내용을 순서대로 보낸다.

TCP는 세그먼트라는 단위로 데이터 스트림을 잘게 나누고, 세그먼트를 IP 패킷이라고 불리는 봉투에 담아서 인터넷을 통해 전달

IP 패킷은 IP 패킷 헤더 + TCP 세그먼트 헤더 + TCP 데이터 조각으로 구성

IP 헤더: 발신지와 목적지 IP 주소, 크기, 기타 플래그

TCP 세그먼트 헤더: TCP 포트 번호, TCP 제어 플래그, 데이터의 순서와 무결성을 검사하기 위해 사용되는 숫자 값 포함

4.1.3 TCP 커넥션 유지하기

TCP는 포트 번호를 통해서 여러 개의 커넥션 유지

TCP 커넥션은 네 가지 값으로 식별 <발신지 IP 주소, 발신지 포트, 수신지 IP 주소, 수신지 포트>

4.1.4 TCP 소켓 프로그래밍

소켓 API를 사용하면, TCP 종단 데이터 구조를 생성하고, 원격 서버의 TCP 종단에 그 종단 데이터 구조를 연결하여 데이터 스트림을 읽고 쓸 수 있다.

4.2 TCP의 성능에 대한 고려

4.2.1 HTTP 트랜잭션 지연

트랜잭션을 처리하는 시간은 TCP 커넥션을 설정하고, 요청을 전송하고, 응답 메시지를 보내는 것에 비하면 상당히 짧다

대부분의 HTTP 지연은 TCP 네트워크 지연 때문에 발생

HTTP 지연 원인

  1. 클라이언트는 URI에서 웹 서버의 IP 주소와 포트 번호를 알아내야 한다.
  2. 클라이언트는 TCP 커넥션 요청을 서버에게 보내고 서버가 커넥션 허가 응답을 회신하기를 기다린다
  3. 클라이언트는 HTTP 요청을 새로 생성된 TCP 파이프틀 통해 전송 및 처리
  4. 웹 서버가 HTTP 응답을 보내는 시간

TCP 네트워크의 지연은 하드웨어의 성능, 네트워크와 서버의 전송 속도, 요청과 응답 메시지의 크기, 클라이언트와 서버 간의 거리에 따라 크게 달라짐

4.2.2 성능 관련 중요 요소

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

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

  1. 클라이언트는 새로운 TCP 커넥션을 생성하기 위해 작은 TCP패킷을 서버에게 전송(SYN)
  2. 서버가 그 커넥션을 받으면 몇 가지 커넥션 매개변수를 산출하고, 커넥션 요청이 받아들여졌음을 의미하는 SYN + ACK를 포함한 패킷을 클라이언트에게 보냄
  3. 클라이언트는 커넥션이 잘 맺어졌음을 알리기 위해서 서버에게 다시 확인응답 신호를 보냄

크기가 작은 HTTP 트랜잭션은 50% 이상의 시간을 TCP를 구성하는 데 씀

4.2.4 확인응답 지연

각 TCP 세그먼트는 순번과 데이터 무결성 체크섬을 가짐

각 세그먼트의 수신자는 세그먼트를 온전히 받으면 작은 확인응답 패킷을 송신자에게 반환

만약 송신자가 특정 시간 안에 확인응답 메시지를 받지 못하면 패킷이 파기되었거나 오류가 있는 것으로 판단하고 데이터를 다시 전송

확인응답은 그 크기 자체가 작기 때문에, TCP는 같은 방향으로 송출되는 데이터 패켓에 확인응답을 편승(piggyback)함

확인응답 지연은 송출한 확인응답을 특정 시간 동안 버퍼에 저장해 두고, 확인응답을 편승시키기 위한 송출 데이터 패킷을 찾는다.

만약 일정 시간 안에 송출 데이터 패킷을 찾지 못하면 확인응답은 별도 패킷을 만들어 전송

HTTP 동작 방식에서는 확인 응답이 송출 데이터 패킷에 편승할 기회를 감소

4.2.5 TCP 느린 시작(slow start)

TCP 커넥션은 시간이 지나면서 자체적으로 ‘튜닝’되어서, 처음에는 커넥션의 최대 속도를 제한하고 데이터가 성공적으로 전송됨에 따라서 속도 제한을 높여나간다. -> TCP 느린 시작(slow start)

TCP 느린 시작은 TCP가 한 번에 전송할 수 있는 패킷의 수를 제한

패킷이 성공적으로 전달되는 각 시점에 송신자는 추가로 2개의 패킷을 더 전송할 수 있는 권한을 얻게 됨

이 혼잡제어 기능 때문에, 새로운 커넥션은 이미 어느 정도 데이터를 주고받은 ‘튜닝’된 커넥션보다 느리다.

4.2.6 네이글(nagle) 알고리즘과 TCP_NODELAY

애플리케이션이 어떤 크기의 데이터든지 TCP 스택으로 전송할 수 있도록, TCP는 데이터 스트림 인터페이스를 제공한다.

TCP가 작은 크기의 데이터를 포함한 많은 수의 패킷을 전송한다면 네트워크 성능은 크게 떨어진다

네이글 알고리즘은 네트워크 효율을 위해서, 패킷을 전송하기 전에 많은 양의 TCP 데이터를 한 개의 덩어리로 합친다.

네이글 알고리즘은 세그먼트가 최대 크기가 되지 않으면 전송을 하지 않는다.

다만 다른 모든 패킷이 확인응답을 받았을 경우에는 최대 크기보다 작은 패킷의 전송을 허락한다.

네이글 알고리즘은 여러 문제를 발생시킴

  1. 크기가 작은 HTTP 메시지는 패킷을 채우지 못하기 때문에, 앞으로 생길지 생기지 않을지 모르는 추가적인 데이터를 기다리며 지연됨
  2. 네이글 알고리즘은 확인응답 지연과 같이 쓰일 경우 형편없음

HTTP 애플리케이션은 성능 향상을 위해서 HTTP 스택에 TCP_NODELAY 파라미터 값을 설정하여 네이글 알고리즘 비활성화 가능

4.2.7 TIME_WAIT의 누적과 포트 고갈

TIME_WAIT 포트 고갈은 성능 측정 시에 심각한 성능 저하를 발생시키지만, 보통 실제 상황에서 발생 X

TCP 커넥션의 종단에서 TCP 커넥션을 끊으면, 종단에서는 커넥션의 IP주소와 포트 번호르 메모리의 작은 영역으로 기록해 놓음

보통 세그먼트의 최대 생명주기의 2배 정도(2MSL)동안 유지

성능 테스트시 포트가 부족하혀 문제가 생길 수 있음

4.3 커넥션 관리

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

HTTP는 클라이언트와 서버 사이에 프락시 서버, 캐시 서버 등과 같은 중개 서버가 놓이는 것을 허락한다.

어떤 경우에는, 두 개의 인접한 HTTP 애플리케이션이 현재 맺고 있는 커넥션에만 적용될 옵션을 지정해야 할 때가 있다.

Connection 헤더에는 다음 세가지 종류의 토큰이 전달될 수 있기 때문에 다소 혼란스러울 수 있다.

  • HTTP 헤더 필드 명은, 이 커넥션에만 해당되는 헤더들을 나열한다
  • 임시적은 토큰 값은, 커넥션에 대한 비표준 옵션을 의미
  • close 값은, 커넥션이 작업이 완료되면 종료되어야 함을 의미

커넥션 토큰이 HTTP 헤더 필드 명을 가지고 있으면, 해당 필드들은 현재 커넥션만을 위한 정보이므로 다음 커넥션에 전달하면 안 됨

Connection 헤더에 있는 모든 헤더 필드는 메시지를 다른 곳으로 전달되는 시점에 삭제되어야 함

HTTP 애플리케이견이 Connection 헤더와 함께 메시지를 전달받으면, 수신자는 송신자에게서 온 요청에 기술되어 있는 모든 옵션을 적용

다음 홉(hop)에 전달하기 전에 Connection 헤더와 Connection 헤더에 기술되어 있던 모든 헤더 삭제

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

순차적으로 처리하면 느리다

커넥션 성능 향상 기법 존재

4.4 병렬 커넥션

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

동시에 처리하여 빠름 -> 지연시간 감소

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

클라이언트의 대역폭이 좁을 때는 대부분 시간을 전송하는 데만 보냄

여려 개의 커넥션을 생성하면서 생기는 부하 때문에 오히려 느려짐

또한 다수의 커넥션은 메모리를 많이 소모하고 자체적인 성능 문제 발생

서버 입장에서 다수의 병렬 커넥션을 허용하지 않음

브라우저는 실제로 병렬 커넥션을 사용하긴 하지만 적은 수의 병렬 커넥션만을 허용

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

화면에 여러 개의 객체가 동시에 보이면서 내려받고 있는 상황을 볼 수 있기 때문에 사용자는 더 빠르게 내려받는 것처럼 느낌

4.5 지속 커넥션

처리가 완료된 후에도 계속 연결 상태로 있는 TCP 커넥션 -> 지속 커넥션

해당 서버에 이미 맺어져 있는 지속 커넥션을 재사용함으로써, 커넥션을 맺기 위한 준비작업에 따르는 시간을 절약할 수 있다.

게다가 이미 맺어져 있는 커넥션은 TCP의 느린 시작으로 인한 지연을 피함으로써 더 빠르게 데이터를 전송할 수 있다.

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

병렬 커넥션의 단점

  • 각 트랜잭션마다 새로운 커넥션을 맺고 끊기 때문에 시간과 대역폭이 소요
  • 각각의 새로운 커넥션은 TCP 느린 시작 때문에 성능이 떨어짐
  • 실제로 연결할 수 있는 병렬 커넥션의 수에는 제한이 있음

지속 커넥션은 커녁센을 맺기 위한 사전 작업과 지연을 줄여주고, 튜닝된 커넥션을 유지하며, 커넥션의 수를 줄여줌

하지만 지속 커넥션을 잘못 관리할 경우, 계속 연결된 상태로 있는 수많은 커넥션이 쌓이게 됨

이는 로컬의 리소스 그리고 원격의 클라이언트와 서버의 리소스에 불필요한 소모를 발생시킴

지속 커넥션은 병렬 커넥션과 함께 쓰일 때 효과적

애플리케션은 적은 수의 병렬 커넥션만을 맺고 그것을 유지

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

keep-alive를 사용하요 커넥션을 맺고 끊는 데 필요한 작업이 없어서 시간 단축

4.5.3 Keep-Alive 동작

HTTP/1.1에서 빠짐

하지만 아직 널리 사용됨

keep-alive 커넥션을 구현한 클라이언트는 커넥션을 유지하기 위해서 요청에 Connection:Keep-Alive 헤더 포함

이 요청을 받은 서버는 그다음 요청도 이 커넥션을 통해 받고자 한다면, 응답 메시지에 같은 헤더를 포함시켜 응답함

4.5.4 Keep-Alive 옵션

클라이언트나 서버나 keep-alive 옵션을 받았다고 해서 무조건 그것을 따를 필요는 없다.

keep-alive 동작은 Keep-Alive 해더의 쉼표로 구분된 옵션들로 제어 가능

  • timeout 파라미터는 Keep-Alive 응답 헤더를 통해 보냄. 커넥션이 얼마간 유지될 것인지를 의미
  • max 파라티터도 응답 헤더를 통해 보냄. 커넥션이 몇 개의 HTTP 트랜잭션을 처리할 때까지 유지될 것인지를 의미
  • 임의의 속성도 지원

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

  • keep-alive는 HTTP/1.0에서 기본으로 사용되지는 않음. Connection:Keep-Alive 헤더를 보내야 함
  • 커넥션을 계속 유지하려면 모든 메시지에 Connection:Keep-Alive 헤더를 포함해 보내야 함
  • 클라이언트는 Connection:Keep-Alive 응답 헤더가 없는 것을 보고 서버가 응답 후에 커넥션을 끊은 것임을 알 수 있음
  • 커넥션이 끊어지기 전에 엔티티 본문의 길이를 알 수 있어야 커넥션 유지 가능
  • 프락시와 게이트웨이는 Connection 헤더의 규칙을 철저히 지켜야 함
  • 정석대로라면, keep-alive 커넥션은 Connection 헤더를 인식하지 못하는 프락시 서버와 맺어지면 안 됨
  • 기술적으로 HTTP/1.0을 따르는 기기로부터 받는 모든 Connection 헤더 필드를 무시해야 함
  • 클라이언트는 응답 전체를 받기 전 커넥션이 끊어졌을 경우, 별다른 문제 없이 요청을 다시 보낼 수 있어야 함

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

Connection 헤더의 무조건 전달

프락시가 이해하지 못하고 그대로 전달함

그대로 전달하여 클라이언트와 서버는 커넥션을 유지하고 있다고 생각하지만, 프락시가 전혀 이해를 못함

그래서 커넥션을 끊지 않고 기다리지만 프락시는 커넥션이 끊어지는 것을 기다림

프락시와 홉별 헤더

Connection 헤더와 Connection 헤더에 명시된 헤더들을 전달하면 안 됨

4.5.7 Proxy-Connection 살펴보기

모든 헤더를 무조건 전달하는 문제를 해결하기 위해 Proxy-Connection 헤더 사용

멍청한 프락시에서는 Connection 헤더가 없기 때문에 문제 X

똑똑한 프락시에서는 Connection헤더로 바꿔서 전달하기 때문에 원하는 기능 수행 가능

이 방식은 클라이언트와 서버 사이에 한 개의 프락시만 있는 경우에서만 동작

4.5.8 HTTP/1.1의 지속 커넥션

keep-alive 대신 설계가 더 개선된 지속 커넥션 지원

지속 커넥션이 HTTP/1.1에서는 기본으로 활성화됨

HTTP/1.1 애플리케이션은 트랜잭션이 끝난 다음 커넥션을 끊으려면 Connection: close 헤더 명시해야 함

클라이언트와 서버는 언제든지 커넥션을 끊을 수 있음

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

  • 클라이언트가 요청에 Connection:close 헤더를 포함해 보냈으면, 클라이언트는 그 커넥션으로 추가적인 요청 불가능
  • 클라이언트가 해당 커넥션으로 추가적인 요청을 보내지 않을 것이라면, 마지막 요청에 Connection:close 헤더를 보내야 함
  • 커넥션에 있는 모든 메시지가 자산의 길이 정보를 정확히 가지고 있을 때에만 커넥션 지속 가능
  • HTTP/1.1 프락시는 클라이언트와 서버 각각에 대해 별도의 지속 커넥션을 맺고 관리해야 함
  • HTTP/1.1 프락시 서버는 클라이언트가 관련 기능에 대한 클라이언트의 지원 범위를 알고 있지 않은 한 지속 커넥션을 맺으면 안 됨
  • 서버는 메시지를 전송하는 중간에 커넥션을 끊지 않을 것이고 커넥션을 끊기 전에 적어도 한 개의 요청에 대해 응답을 할 것이긴 하지만, HTTP/1.1에서는 Connection 헤더와 상관없이 언제든지 커넥션을 끊을 수 있음
  • HTTP/1.1 애플리케이션은 중간에 끊어지는 커넥션을 복구할 수 있어야 함
  • 클라이언트는 전체 응답을 받기 전에 커넥션이 끊어지면, 요청을 반복해서 보내도 문제가 없는 경우에는 요청을 다시 보낼 준비가 되어 있어야 함
  • 하나의 사용자 클라이언트는 서버의 과부하를 방지하기 위해서, 넉넉잡아 두 개의 지속 커넥션을 유지해야 함

4.6 파이프라인 커넥션

HTTP/1.1은 지속 커넥션을 통해서 요청을 파이프라이닝할 수 있다.

왕복으로 인한 시간을 줄여서 성능을 높여줌

파이프라인 제약사항

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

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

4.7.1 ‘마음대로’ 커넥션 끊기

4.7.2 Content-Length와 Truncation

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

커넥션은 언제든 끊을 수 있다.

HTTP 애플리케이션은 예상치 못하게 커넥션이 끊어졌을 때에 적절히 대응할 수 있는 준비가 되어 있어야 함

제전송으로 인한 부작용을 조심해야 함

한 번 혹은 여러 번 실행됬는지에 상관없이 같은 결과를 반환한다면 멱등(idempotent)하다고 함

GET, HEAD, PUT, DELETE, TRACE, OPTIONS 메서드들은 멱등하다.

비멱등인 요청을 다시 보내야 한다면, 이전 요청에 대한 응답을 받을 때까지 기다려야 함.

4.7.4 우아한 커넥션 끊기

전체 끊기와 절반 끊기

TCP 끊기와 리셋 에러

기기들에 예상치 못한 쓰기 에러를 발생하는 것을 예방하기 위해 ‘절반 끊기’를 사용해야 함

보통은 커넥션의 출력 채널을 끊는 것이 안전

커넥션의 반대편에 있는 기기는 모든 데이터를 버퍼로부터 읽고 나서 데이터 전송이 끝남과 동시에 커넥션을 끊었다는 것을 알게 됨

클라이언트에서 더는 데이터를 보내지 않을 것임을 확인할 수 없는 이상, 커넥션의 입력 채널을 끊는 것은 위험하다

클라이언트에서 이미 끊긴 입력 채녈에 데이터를 전송하면 TCP는 ‘connection reset by peer’메시지를 보냄

그러면, 버퍼에 저장된 데이터 모두 삭제 -> 파이프라인 커넥션 악화

우아하게 커넥션 끊기

애플리케이션이 자신의 출력 채널을 먼저 끊고 다른 쪽에 있는 기기의 출력 채널이 끊기는 것을 기다리는 것

양쪽에서 데이터를 전송하지 않을 것이라고 알려주면 커넥션은 리셋의 위험 없이 온전히 종료됨

참조

  1. HTTP 완벽 가이드
This post is licensed under CC BY 4.0 by the author.