1부 HTTP : HTTP 메시지
04. 커넥션 관리
🔔 오늘의 학습 내용
- HTTP는 어떻게 TCP 커넥션을 사용하는가?
- TCP 커넥션의 지연, 병목, 막힘
- 병렬 커넥션, keep-alive 커넥션, 커넥션 파이프라인을 활용한 HTTP의 최적화
- 커넥션 관리를 위해 따라야 할 규칙들
🎨 01. TCP 커넥션
- TCP 스트림은 세그먼트로 나뉘어 IP 패킷을 통해 전송된다.
- HTTP가 메세지를 전송하려고 할 경우, 현재 연결되어 있는 TCP 커넥션을 통해서 메세지 데이터의 내용을 순서대로 보낸다.

- 컴퓨터는 항상 TCP 커넥션을 여러개 가지고 있고, 4가지 값으로 식별한다.
- 발신지 IP주소
- 발신지 포트
- 수신지 IP주소
- 수신지 포트
📌 01-4. TCP 소켓 프로그래밍
- 소켓 API를 사용하면 TCP 종단 데이터 구조를 생성하고, 원격 서버의 TCP 종단에 그 종단 데이터 구조를 연결하여 데이터 스트림을 읽고 쓸 수 있다.
- 기본적인 네트워크 프로토콜의 핸드셰이킹, TCP데이터 스트림, IP패킷간의 분할 및 재조립에 대한 모든 내용을 외부로부터 숨긴다.
🎨 02. TCP의 성능에 대한 고려
🔔 HTTP 트랜잭션 지연은 대부분 TCP 네트워크 지연때문에 발생한다.

- 클라이언트는 URI에서 웹 서버의 IP 주소와 포트 번호를 알아내야 한다.
- DNS 캐시가 없다는 가정 하에, 만약 호스트에 방문한 적이 없다면 DNS 인프라를 사용해 IP 주소로 변환하는데 수십 초의 시간이 걸릴 것이다.
- 그러나 요즘엔 대부분 밀리초 단위로 DNS분석이 끝난다.
- 클라이언트는 TCP 커넥션 요청을 서버에게 보내고, 서버가 커넥션 허가 응답을
회신하기를 기다린다.
- 커넥션 설정 시간은 새로운 TCP 커넥션에서 항상 발생한다.
- 커넥션이 맺어지면 클라는 HTTP 요청을 새로 생성한 TCP 파이프를 통해 전송한다.
- 웹 서버는 데이터가 도착하는 대로 TCP 커넥션에서 요청 메세지를 읽고 처리한다.
📌 02-3. TCP 핸드셰이크 지연 (3 handshake)
🔔 어떤 데이터를 전송하든, 새로운 TCP 커넥션을 열 때면,
TCP 소프트웨어는 커넥션을 맺기 위한 조건을 맞추기 위해 연속으로 IP 패킷을
교환한다. TCP 커넥션이 핸드셰이크를 하는 순서를 알아보자.

- 클라이언트는 새로운 TCP 커넥션을 생성하기 위해 작은 TCP 패킷을 서버에게 보낸다. 이는 커넥션 생성 요청을 뜻한다.
- 그 패킷은 SYN(synchronize sequence numbers)라는 특별한 flag를 가진다.
- TCP flags의 종류 중 하나이다.
- 서버가 그 커넥션을 받으면 몇 가지 커넥션 매개변수를 산출한다.
- 커넥션 요청이 받아들여졌음을 의미하는 SYN + ACK(acknowledgment) flag를 포함한 TCP 패킷을 클라이언트에게 보낸다.
- 마지막으로 클라는 커넥션이 잘 맺어졌음을 알리기 위해서 서버에게 다시 확인 응답 신호를 보낸다. (ACK)
- 오늘날 TCP는 클라이언트가 이 확인 응답 패킷과 함께 + 데이터를 보낼 수 있다.
- 실제 개발자들은 이 패킷들을 보지 못한다.
- TCP 커넥션이 생성될 때 발생하는 지연이 전부다.
- 크기가 작은 HTTP 트랜잭션은 50% 이상의 시간을 TCP를 구성하는 데 쓴다.
📌 02-5. TCP의 느린 시작 (slow start)
- 전송속도는 커넥션이 만들어진 지 얼마나 지났는지에 따라 달라질 수 있다.
- 시간이 지나면서 자체적으로 튜닝되어서, 처음에는 커넥션의 최고속도를 제한하고 데이터가 성공적으로 전송됨에 따라서 속도 제한을 높여나간다.
- 갑작스러운 부하와 혼잡을 방지하는 데 쓰인다.
- 이 혼잡 제어 기능 때문에, 새로운 커넥션은 이미 어느정도 데이터를 주고받은 튜닝된 커넥션보다 느리다.
- 지속 커넥션이란, 이미 존재하는 커넥션을 재사용하는 기능을 뜻한다.
📌 02-6. 네이글(Nagle)알고리즘과 TCP_NODELAY
- 각 TCP 세그먼트는 40바이트 상당의 플래그와 헤더를 포함하여 전송하기 때문에, TCP가 작은 크기의 데이터를 포함한 많은 수의 패킷을 전송한다면 네트워크 성능을 크게 떨어진다.
- 네이글 알고리즘 은 네트워크 효율을 위해서, 패킷 전송하기 전에 많은 양의 TCP 데이터를 한 개의 덩어리로 합친다.
- 네이글 알고리즘은 세그먼트가 최대크기가 되지 않으면 전송을 하지 않는다.
- 다만 다른 모든 패킷이 확인응답을 받았을 경우에는 최대 크기보다 작은 패킷의 전송을 허락한다.
📌 네이글 알고리즘은 HTTP 성능 관련해 여러 문제를 발생시킨다.
- 크기가 작은 HTTP 메세지는 패킷을 채우지 못하기 때문에 다 채울 때 까지 기다리며 지연이 될 수 있다.
- 네이글 알고리즘은 확인응답 지연과 함께 쓰일 경우 형편없이 작동한다.
- 지연 알고리즘은 확인응답을 100~200 밀리초 지연시킨다.
📌 02-7. TIME_WAT의 누적과 포트 고갈
- TCP 커넥션의 종단에서 TCP커넥션을 끊게 된다면 종단에서는 커넥션의 IP주소와 포트 번호를 메모리의 작은 제어 영역에 기록해놓는다.
- 이 정보는 같은 주소와 포트 번호를 사용하는 새로운 TCP 커넥션이 일정시간 동안에는 생성되지 않게 하기위해 기록해놓는것이다.
- 이는 이전 커넥션과 관련된 패킷이 그 커넥션과 같은 주소와 포트 번호를 가지는 새로운 커넥션에 삽입하는 문제를 방지한다.
🎨 03. HTTP 커넥션 관리
📌 03-1. 흔히 잘못 이해하는 Connection 헤더
- 어떤 경우에는 두개의 인접한 HTTP 애플리케이션이 현재 맺고 있는 커넥션에만 적용될 옵션을 지정해야 할 때가 있다.
- HTTP Connection 헤더 필드는 커넥션 토큰을 쉼표로 구분하여 가지고 있으며, 다른 커넥션에 전달되지 않는다.
- 커넥션 토큰이 HTTP 헤더 필드 명을 가지고 있으면 해당 필드들은 현재 커넥션만을 위한 정보이므로 다른 커넥션에 전달되서는 안된다.
- Connection 헤더는 hop-by-hop 헤더 명을 기술하는데, 이것을 헤더 보호기 라고한다.
- HTTP 애플리케이션이 Connection 헤더와 함께 메세지를 전달받으면, 수신자는 송신자에게서 온 요청이 기술 되 어있는 모든 옵션을 적용한 다음 hop에 있는 메세지를 전달하기전에 Connection헤더와 헤더에 기술되어있던 모든 헤더를 제거한다.
📌 03-2. 순차적인 트랜잭션 처리에 의한 지연

- 커넥션 관리가 재대로 이루어지지 않으면 TCP 성능이 안좋아 질 수 있다.
- 트랜잭션이 새로운 커넥션을 필요로 한다면, 커넥션을 맺는데 발생하는 지연과 함께 느린 시작 지연이 발생할 것 이다.
- 순차적으로 로드하는 방식이 또 하나의 문제점은, 특정 브라우저의 경우 객체를 화면에 배치하려면 객채의 크기를 알아야 하기 때문에 모든 객체를 내려받기 까지는 텅 빈 화면을 이용자에게 보여준다.
📌 03-3. 병렬 커넥션
- 단일 커넥션의 대역폭 제한과 커넥션이 동작하지 않고 있는 시간을 활용하면, 객체가 여러 개 있는 웹페이지를 더 빠르게 내려받을 수 있을 것 이다.
💎 병렬 커넥션이 항상 더 빠르지는 않다.
- 병렬 커넥션이 일반적으로 더 빠르기는 하지만 항상 그렇지는 않다.
- 다수의 커넥션은 메모리를 많이 소모하고 자체적인 성능 문제를 발생시킨다.
- 브라우저는 실제로 병렬 커넥션을 사용하긴 하지만 적은 수의 병렬 커넥션만 허용한다.
- 서버는 특정 클라이언트로부터 과도한 수의 커넥션을 맺을 경우 그것을 임의로 끊을 수 있다.
💎 병렬 커넥션은 더 빠르게 느껴 질 수 있다.
- 병렬 커넥션이 페이지를 더 빠르게 로드하지는 않는다.
- 하지만 병렬커넥션이 화면에 여러 개의 객체가 도시에 보이면서 내려 받는 상활을 볼 수 있기 때문에 사용자는 더 빠르게 내려받고 있는 것 처럼 느낄 수 있다.
📌 03-4. 지속 커넥션
- 서버에 HTTP 요청을 하기 시작한 애플리케이션은 웹페이지 내의 이미지 등을 가져오기 위해서 그서버에게 또 요청을 하게 된다.
- 이 속성을 사이트 지역성이라 부른다.
- HTTP/1.1 을 지원하는 기기는 처리가 완료 된 후에도 TCP 커넥션을 유지하여 앞으로 있을 HTTP 요청에 재사용 할 수 있다.
- 처리가 완료된 후 에도 커넥션을 연결된 상태로 있는 TCP 커넥션을 지속 커넥션 이라고 한다.
- 지속 커넥션을 사용함으로써 커넥션을 맺기 위한 준비 작업, 예를 들면 핸드 세이크 등과 같이 시간을 절약 할 수 있다.
📌 03-5. 지속 커넥션 vs 병렬 커넥션
🔔 병렬 커넥션은 여러 객체가 있는 페이지를 더 빠르게 전송한다.
그러나 병렬 커넥션에는 다음과 같은 단점이 존재한다.
- 각 트랜잭션마다 새로운 커넥션을 맺고 끊기 때문에 시간과 대역폭이 소요된다.
- 각 새로운 커넥션은 TCP 느린 시작 때문에 성능이 떨어진다.
- 실제로 연결 할 수있는 병렬 커넥션의 수 에는 제한이 있을 수 있다.
🔔 반대로 지속 커넥션은 병렬 커넥션에비해 몇가지 장점이 있다.
- 커넥션을 맺기위한 사전 작업과 지연시간을 줄여준다.
- 튜닝된 커넥션을 유지하며 커넥션의 수를 줄여준다.
- 하지만 지속 커넥션을 잘못 관리할 경우에는 계속 연결된 상태로 있는 수 많은 커넥션이 쌓이게 될 것 이다.
📌 03-6. HTTP/1.0+ 의 Keep-alive 커넥션
- 많은 HTTP/1.0 브라우저와 서버들은 일찍부터 다소 실험적인 Keep-alive 커넥션이라는 지속 커넥션을 지원하기 시작하였다.
- 초기의 커넥션은 상호 운용과 관련된 설계 문제가 있었지만 HTTP/1.1 에 수정되었다.
💎 Keep-alive 동작
- HTTP/1.0 Keep-alive 커넥션을 구현한 클라이언트는 커넥션을 유지하기 위해서 요청에 Connection: Keep-alive 헤더를 포함시킨다.
- 해당 헤더가 없으면 클라이언트는 서버가 keep-alive를 지원하지 않으며, 응답 메세지가 전송되고 나면 서버가 커넥션을 끊을 것이라고 추정한다.
💎Keep-alive 옵션
- Keep-alive 헤더는 커넥션을 유지하기를 바라는 요청일 뿐이다.
- 서버가 Keep-alive 요청을 받았다고 해서 무조건 그 것을 따를 필요는없다.
- keep-alive의 동작은 keep-alive 헤더의 쉼표로 구분된 옵션들로 제어 가능하며 선택 사항이다.
- timeout 파라미터는 keep-alive 헤더를 통해보냄. 이는 얼마나 유지될 것인지를 의미한다.
- max 파라미터는 keep-alive 응답 헤더를 통해보냄 이는 커넥션이 몇 개 HTTP 트랜잭션을 처리할 때 까지 유지 할 것인지 의미한다.
💎 keep-alive 커넥션 제한과 규칙
- Keep-alive는 HTTP/1.0에서 기본적으로 사용되지는 않는다.
- 커넥션을 계속 유지하려면 모든 메세지에 Connection: Keep-alive 헤더를 포함해야한다.
- 클라이언트는 Connection: Keep-alive 응답 헤더가 없는 것을 보고 서버가 응답 후에 커넥션을 끊을 것임을 알 수 있다.
- 커넥션이 끊어지기 전에 엔티티 본문의 길이를 알 수 있어야 커넥션을 유지할 수 있다.
- 본문이 정확한 Content-Length값과 함께 멀티 파트 미디어 형식을 가지거나 청크 인코딩으로 인코드가 되어야함을 뜻한다.
- 이것을 통해 트랜잭션이 끝나는 시점에 기존 메세지의 끝과 새로운 메세지 시작을 알 수 있다.
- 클라이언트는, 응답 전체를 모두 받기 전에 커넥션이 끊어졌을 경우, 별다른 문제가 없으면 요청을 다시 보낼 수 있게 준비되어 있어야한다.
💎 HTTP/1.1 의 지속 커넥션
- HTTP1/1. 에서는 keep-alive 커넥션을 지원하지 않는 대신, 설계가 더 개선된 지속 커넥션을 지원한다.
- 지속 커넥션의 목적은 keep-alive 커넥션과 같지만 그에 비해 더 잘 동작한다.
- HTTP/1.1의 지속 커넥션은 기본으로 활성화 되어 있다.
- HTTP/1.1 애플리케이션은 트랜잭션이 끝난 다음 커넥션을 끊으려면 Connection: close 헤더를 명시해야한다.
💎 지속 커넥션의 제한과 규칙
- 클라이언트가 요청에 Connection이란, close 헤더를 포함해 보냈으면 클라이언트는 그 커넥션으로 추가적인 요청을 보낼수가 없음을 뜻한다.
- 클라이언트가 해당 커넥션으로 추가적인 요청을 보내지 않을 것이라며 요청에 Connection: close 헤더를 보내야한다.
- 커넥션에 있는 모든 메세지가 자신의 길이(Content-Length) 정보를 정확히 가지고 있을 때만 커넥션을 지속 시킬 수 있다.
- HTTP/1.1 프락시는 클라이언트와 서버 각각 에 대한 별도의 지속 커넥션을 맺고 관리해야한다.
- HTTP/1.1 프락시 서버는 클라이언트가 커넥션 관련 기능에 대한 클라이언트의 지원 범위를 알고 있지 않은 한 지속 커넥션을 맺으면 안된다.
- HTTP/1.1 애플리케이션은 중간에 귾어지는 커넥션을 복구 할 수 있어야한다.
📌 03-7. 파이프라인 커넥션
- HTTP/1.1은 지속 커넥션을 통해서 요청을 파이프라이닝 할 수 있다.
- 이는 지속 커넥션의 성능을 더 높여준다.
- 여러 개의 요청은 응답이 도착하기 전까지 큐에 쌓이고 첫 번째 요청이 네트워크를 통해 전달되면 거기에 이어 두 번째 세 번째 요청이 전달될 수 있다.
- 파이프라인 커넥션을 사용하기전에 여러가지 제약 사항이 존재한다.
- HTTP클라이언트는 커넥션이 지속 커넥션인지 확인 하기 전까지 파이프라인을 이어서는 안된다.
- HTTP 응답은 요청 순서와 같게 와야함.HTTP 메세지는 순번이 매겨 있지않아서 응답이 순서 없이 오면 순서에 맞게 정렬 시킬 방법이 없다.
- HTTP 클라이언트는 커넥션이 언제 끊어지더라도, 완료되지 않은 요청이 파이프라인에 있으면 안된다.
- HTTP 클라이언트는 POST요청 같이 반복해서 보낼 경우 문제가 생기는 요청은 파이프라인을 통해 보내면 안된다.
📌 03-7. 커넥션 끊기에대한 미스터리
🔔 커넥션 관리(언제 어떻게 커넥션을 끊는가)에는 명확한 기준이 없다.
💎 마음대로 커넥션 끊기
- 어떠한 HTTP클라이언트, 서버, 혹은 프락시든 언제든지 TCP 전송 커넥션을 끊을 수 있다.
- 지속 커넥션이 일정 시간 동안 요청을 전송하지 않고 유휴 상태 에 있으면 서버는 그 커넥션을 끊을 수 있다.
💎 Content-Length
- HTTP 응답은 본문의 정확한 크기 값을 가지는 Content-Length 헤더를 가지고 있어야 한다.
💎 커넥션 끊기허용, 재시도, 멱등성
- 커넥션은 심지어 에러가 없더라도 언제든지 끊을 수 있다.
- HTTP 애플리케이션은 예상치 못하게 커넥션이 끊어졌을 때에 적절히 대응 할 수 있는 준비가 되어 있어야한다.
- 어떤 요청 데이터가 전송되었지만 응답이 오기전에 커넥션이 끊기면 클라이언트는 실제로 서버에 얼마만큼 요청이 처리되었는지 전혀 알 수 없다.
- 한 번 혹은 여러 번 실행 됬는지에 상관업이 같은 결과를 반환한다면 그 트랜잭션은 멱등한다 라고한다.
- GET, HEAD, PUT, DELETE, TRACE 그리고 OPTIONS 메서드들은 멱등하다고 이해하면 된다.
- 클라이언트는 POST와 같이 멱등 아닌 요청은 파이프라인을 통해 전송하면 안된다.