[HTTP 완벽 가이드] 커넥션 관리

Urther·2022년 7월 31일
0

HTTP 완벽 가이드

목록 보기
4/4
post-thumbnail

HTTP 완벽 가이드를 읽고 정리한 글입니다 📒

4장 커넥션 관리

이 장에서는 HTTP가 어떻게 TCP 커넥션을 사용하는지, TCP 커넥션의 지연 방법을 확인하고 HTTP를 최적화 시키는 방법에 대해 설명하고 있는 장이다.

TCP 커넥션

HTTP 는 프로토콜들의 계층화된 집합인 TCP/IP 를 통해 이루어진다. 전 세계 어디에서는 TCP/IP 커넥션을 맺을 수 있다. 그리고 커넥션이 이루어지면 클라언트와 서버 사이에 주고 받은 메시지들은 손상 되거나 순서가 바뀌지 않고 전달된다.

통신 과정

사용자가 만약 죠의 컴퓨터 가게에서 전동 공구의 최신 가격 목록을 가져온다고 가정한다.

http://www.joes-hardware:80/power-tools.html

(1) 브라우저가 주소에서 Host 명(www.joes-hardware)을 추출한다.
(2) 브라우저가 이 호스트 명에 대한 IP 주소를 찾는다 ( DNS를 통해 )
(3) 브라우저가 Port 번호 (80)을 얻는다.
(4) 브라우저가 IP 주소의 80 포트로 TCP 커넥션을 생성한다.
(5) 연결이 된 것을 확인하고, 서버로 HTTP 메시지를 보낸다.
(6) 서버에서 요청을 처리하고, 응답을 보낸다.
(7) 브라우저에 HTTP 응답 메시지를 확인하고, 커넥션을 끊는다.

TCP 스트림

TCP 는 IP 패킷이라는 작은 조각을 통해 데이터를 전송한다. HTTPS는 HTTP와 달리 암호화된 TSL 혹은 SSL 부분이 추가 되어있다.

HTTP가 메시지를 전송하고자 할 때 TCP 세그먼트는 IP 패킷에 담겨서 보내지게 되는데 이 때, HTTP 프로그래머는 직접 이 패킷을 확인할 수 없다.

TCP 성능에 대한 고려

HTTP가 왜 TCP의 성능과 연관이 있을까?

HTTP는 TCP 바로 위에 있는 계층이다. 따라서 HTTP 트랜잭션의 성능은 TCP 성능에 영향을 받는다. 쉽게 말해서, TCP 성능이 저하된다면 자연스레 HTTP 트랜잭션 (request, response) 또한 저하된다.

실제로 서버에서 요청에 대한 처리(보라색)에 비해 TCP 커넥션 설정과 요청, 응답에 걸리는 시간이 오래 걸리는 것을 확인할 수 있다.

단, 많은 자원의 요청이 있을 경우 HTTP 지연이 원인일 수 있다. 하지만, 그렇지 않은 경우 HTTP 의 지연은 TCP의 지연 때문이다.

TCP 의 3 way-handshake

(1) SYN : 클라이언트가 Server에게 TCP 커넥션 요청을 한다.
(2) SYN + ACK : 서버가 커넥션 요청을 받는다.
(3) ACK : 클라이언트가 확인했다는 응답을 보낸다.

TCP 부하 방지

1 ) Piggyback

A라는 사용자가 B에게 TCP 커넥션 요청을 보낸다. 그리고 B는 커넥션 요청에 대한 응답을 A에게 또 보낸다. 그 요청을 잘 받았다고 A는 B에게 알려주는 방법이 두 가지가 있다.

  1. ACK 을 보내고 HTTP 메시지를 보내는 방법 ( 통신을 2 번 해야한다. )
  2. ACK과 함께 HTTP 메시지를 함께 보낸다.

PiggyBack 의 장점은 한꺼번에 보내는 만큼 네트워크를 조금 더 효율적으로 사용할 수 있다는 점이다. 그러나, A가 SYN+ACK을 받지 않은 줄 알고 B가 재응답하는 일이 생길 수 있다.

2) Slow start
처음 1 RTT(request 하고, response 받아오는 주기) 일 때는 한 개의 패킷만 보낼 수 있다가, 요청이 실패하지 않는다면 2개로 늘리고, 또 실패하지 않는다면 4개로 늘린다(TCP 커넥션 자체적으로 튜닝한다) 만약 패킷의 loss가 발생한다면 늘리지 않고 절반으로 줄여주는 알고리즘이다.

당연히, 초기의 커넥션 일 때와 달리 커넥션이 지속되면 될 수록 빨라진다는 장점이있지만 배수 단위로 급증하기 때문에 초기 커넥션일 때 그렇게 크리티컬하지 않다.

3 ) Nagle 알고리즘



TCP 세그먼트는 UDP 세그먼트에 비해 상당한 flag와 header가 존재한다. 만약 작은 data를 보내야하고, 여러 번 보내야한다면 어떻게 될까? 작은 데이터를 위해 무거운 TCP 세그먼트를 여러 번 보내야하기 때문에 (심지어 ACK를 통해 데이터를 받았는지 확인하는 절차도 있어야한다.) 네트워크의 성능에 무리가 있을 것이다.

따라서, Nagle 알고리즘이 생겼다. 네이글 알고리즘은 TCP 세그먼트에 들어갈 수 있는 data의 크기만큼 차지 않으면 전송을 하지 않는 방법이다.

제법 효율적인 방법인 것 처럼 느껴지지만, 효율적이지 않다. HTTP 메시지는 크기가 작은 경우가 많다. 만약 언제 추가적으로 생길지 모르는 데이터를 위해 네이글 알고리즘은 기다려야하고 그만큼 지연이 발생할 수 있다.

HTTP의 커넥션 관리

1 ) connection header

HTTP의 커넥션 헤더는 3가지 종류의 토큰이 존재한다.

  1. HTTP Header 필드명
  2. 임시 토큰
  3. close(커넥션이 작업 완료 되면 종료한다)

특징은 hop-by-hop이라는 것이다. hop-by-hop이라는 의미는 클라이언트와 서버 사이에 중간 매개체 같은 것들이 존재한다. 예를 들면 프록시가 될 수 있다.

Connection 헤더를 통해 meter는 다른 hop(커넥션)으로 전달하면 안되고, 'bill-my-credit-card' 옵션을 적용할 것이며, 해당 트랜잭션이 끝나면 커넥션을 끊을 것이라고 명시

Connection에 담겨있는 헤더 값들은 다음 커넥션에 영향을 끼쳐선 안되기 때문에 현재 커넥션에 있는 헤더 값들을 모두 삭제 한 후 다음 커넥션 요청을 진행해야 한다.

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

만약에 웹 페이지에 2개의 이미지 파일이 있다고 가정해본다면 3번의 트랜잭션이 일어난다. (1트랜잭션 - html 파일 , 2와 3트랜잭션 이미지 파일)

화면에 이미지를 보여주기 위해서는 객체라는 것에 담아 표시를 하는데 객체의 크기를 알아야 한다. 따라서 이미지 2개가 모두 다운 받을 때까지 사용자는 빈 화면을 보고 있어야 하는 단점이 있다. (이미지 2개 정도야 하겠지만, 이미지가 많아지는 페이지라면? 사용자는 더 기다려야할 것이다.)

따라서, 이 문제를 해결하기 위해 4가지 해결안을 제안한다.

  1. 병렬 커넥션
  2. 지속 커넥션
  3. 파이프라인 커넥션
  4. 다중 커넥션

( 1 ) 병렬 커넥션

HTTP 클라이언트는 여러 개의 커넥션을 맺을 수 있다. 순차 커넥션보다 병렬적으로 다운 받을 수 있기 때문에 빠르다. 여러 개의 객체들이 다운 되어 화면에 그려지는 것이 보이기 때문에 사용자 입장에서도 더 빠르다고 느껴질 수 있다.

그러나, 반드시 빠른 것은 아니다.
클라이언트의 네트워크의 대역폭이 좁은 경우 데이터 전송에 굉장히 오랜 시간이 걸린다. 그리고 이미지가 100개라고 100개의 커넥션을 서버와 맺으면 안된다. 메모리가 많이 소모되고 자체적인 성능의 이슈가 발생할 수 있다.

예를 들어, 100명의 클라이언트가 100개의 커넥션을 맺었다면 서버는 10,000개의 커넥션을 관리해야한다고 생각하면 이해가 빨리 될 수 있다.

따라서 사용자에게 적은 수의 커넥션만 연결 될 수 있도록 한다.

( 2 ) 지속 커넥션

첨부 된 이미지 혹은 하이퍼링크가 동일 페이지일 수 있다. 만약 그런 경우 또 서버에 요청하고 TCP slow start 과정을 맺어야하는 귀찮은 과정이 있다. ⏤ 각 처리가 끝나면 연결을 끝내는 비지속 커넥션 방법 ⏤ 따라서 클라이언트가 연결을 끝낼 때까지 커넥션을 계속 유지하면서 재사용하는 지속 커넥션 방법이 등장한다.

지속 커넥션 VS 병렬 커넥션
병렬 커넥션은 여러 객체가 있는 페이지에 빠른 전송이 가능하게끔 한다. 그러나 병렬 커넥션에는 단점이 존재한다.

1️⃣ 각 트랜잭션 마다 연결을 끊고 맺어야한다.
▶️ 새로운 커넥션이 생길 경우 TCP Slow start로 인해 성능이 저하될 가능성이 있다.
2️⃣ 병렬 연결의 수가 제한 되어 있다.

지속 커넥션과 같은 경우 커넥션을 맺기 위한 사전 작업이나 지연이 낮다. 그리고 튜닝된 커넥션(TCP Slow start에서 2배씩 늘려놓은 상태)에서 유리하다. 그러나, 이런 지속 커넥션에도 단점이 있다.

1️⃣ 지속된 커넥션을 관리하지 못할 경우 많은 커넥션들이 쌓이게 되고, 불필요한 소모로 이어진다.

따라서 ! 지속 커넥션과 병렬 커넥션을 섞어서 사용한다.

1️⃣ HTTP/1.0+ Keep Alive

커넥션을 맺고 끊는데 필요한 작업을 하지 않기 때문에 시간이 단축된다.

  • Keep-Alive 속성 설정
// 요청
GET / index.html HTTP/1.0
Host : www.joes-hardware.com
Connection : Keep-Alive
// 응답
HTTP/1.0 200 OK 
Content-type: text/html
Content-length:3104
Connection : Keep-Alive
...

만약, 응답에 Keep-Alive 속성이 없다면 클라이언트는 서버가 Keep-Alive 속성을 지원하지 않으며, 서버 커넥션을 끊을 것이라고 추정한다.

  • Keep-Alive 옵션
Connection: Keep-Alive
Keep-Alive : max=5, timeout=120

keep-alive 속성을 2분간 유지하라는 의미이다.

  • 멍청한 프락시 (Dumb Proxy)

프록시는 Connection 헤더를 이해하지 못한다. 따라서 Connection 헤더를 삭제하지 않고 요청을 다음 Hop으로 전달한다.

이 때, 발생하는 문제가 존재한다. 클라이언트와 서버는 Keep-Alive 속성이 설정되었다고 믿는다. 클라이언트가 서버에 요청을 보내게 될 때 프록시는 같은 Connection 상에서 요청 오는 것을 예측하지 못하기 때문에 요청이 무시되고, 클라이언트 브라우저는 요청을 무한히 기다려야한다는 문제점이 있다.

이런 이유 때문에 프락시는 Connection 헤더와 Connection 헤더에 명시 되어있는 것들을 정리하면 안된다. (위의 Connection Header 파트 참고)

다른 방법도 고안되었는데 그 방법은 Proxy-Connection이다. 프락시가 무조건 서버에 Connection 헤더를 전달하더라도 웹 서버는 Proxy-Connection을 인식하지 못하기 때문에 괜찮다.

그러나 위와 같은 방법은 한 개의 프락시가 있을때만 유효한 방법이다. 만약, 영리한 프락시(Proxy-Connection 을 읽고 Connection 으로 전달해주는) 옆에 멍청한 프락시 (Proxy-Connection 헤더를 인식하지 않고 바로 웹 서버에 헤더 전달)하는 것이 있다면 문제가 된다.

2️⃣ HTTP/1.1 지속 커넥션

위의 Keep-Alive 방식과 달리 HTTP/1.1 지속 커넥션은 별도의 설정을 해주지 않는다면 기본 값이 지속 커넥션을 유지하는 방법이다.

트랜잭션이 끝난 다음 커넥션을 끊으려면 Connection : close를 명시해준다. 만약 위와 같은 헤더를 보내지 않는다면 커넥션을 계속 유지하겠다는 것으로 인식한다.

- 파이프라인 커넥션

실행 중인 명령이 끝나기 전에 다른 명령을 요청할 수 있음

HTTP 1.1 은 지속 커넥션을 통해 파이프라이닝이 가능하다.

profile
이전해요 ☘️ https://mei-zy.tistory.com

0개의 댓글