http란
웹상에서 정보를 주고받기 위한 규약이다. 과거엔 html문서를 위주로 정보를 송수신하기 위한 목적으로 만들었다. 그러나 보내는 데이터 타입과 양이 많아지면서 데이터를 보내는 양을 줄이고 통신 속도를 빠르게 하는 방향으로 발전되고 있다.
http 특징
http의 특징은 2가지이다.
1. Stateless
상태를 저장하지 않는다. 같은 컴퓨터에서 여러번의 동일한 http요청을 보내도 쿠키나 세션같은 사용자를 식별할 수 있는 값을 같이 보내지 않는 이상 요청을 수신한 서버는 요청자가 동일한 사람인지를 알 수 없다. 또한 요청을 보낸 서버가 같은 서버라고 보장하지 않는다.
참고: Stateful
- stateless의 반의어
- 상태를 저장하여 서로 다른 요청을 하여도 상태와 같이 보내서 사용자를 식별할 수 있다.
2. Connectionless
요청과 응답을 완료하면 연결 유지하지 않고 끊는다. http요청은 서버에 요청을 하고 응답을 받으면 연결을 종료시킨다.
참고: Connection Oriented
- Connectionless의 반의어
- 요청과 응답을 완료하여도 연결을 유지한다.
- 소켓통신 처럼 요청과 응답을 모두 수행하여도 연결 종료를 요청하지 않는 한 계속 유지한다.
http 메소드
클라이언트가 서버에게 요청하는 동작이다. 종류는 다음과 같다.
- GET : 자원을 조회
- POST: 자원을 생성
- DELETE: 자원을 제거
- PATCH: 자원을 모두 변경
- PUT: 자원을 일부 변경
- OPTIONS: 자원을 요청하는 서버의 옵션을 조회
물론 예외적인 경우도 있다. 바로 로그인 과정이다. 로그인을 할 때 GET과 POST중 어느 메소드를 사용하냐고 검색해보면 POST를 사용한다. 근데 생각해보면 로그인은 이미 존재하는 사용자라는 자원을 조회하는 행위인데 왜 GET이 아니라 POST를 사용할까를 생각해볼 수 있다.
보안적인 이유로 POST를 이용한다는 답변도 있다. GET메소드 요청은 URL에 정보가 남기 때문에 브라우저 로그 파일을 탈취당하면 URL만으로 로그인 정보를 알 수 있다. 혹은 CSRF공격으로 원치않는 요청을 보내서 악의적인 행위를 수행시킬 수 있기에 보안의 목적으로 POST를 이용한다는 대답도 있다.
그러나 보안적인 이유로만으로 사용되진 않는다. 로그인 정보를 숨기는 목적이라면 다른 메소드들도 가능하다. tcp/ip 관점으로 보자면 위의 이유로 보안을 강화하는 목적은 4계층인 응용계층에서만 해당되는 내용이다. 3계층인 전송계층에서는 별도의 보안처리 없는 단순 http요청은 메소드 상관없이 패킷내용을 분석해서 볼 수 있다. 즉 4계층에 대한 어느 수준의 보안을 제공하는 것일 뿐이지 이하의 계층에서는 POST를 쓰든 GET을 쓰든 둘다 똑같이 취약할 뿐이다. 즉 보안적인 이유만으론 설명할 수 없다.
또 다른 이유로는 멱등성이 없다(비멱등성) 는 이유이다. 멱등성(idempotent)이란 동일한 연산을 연속으로 수행해도 결과가 달라지지 않는 성질을 의미한다.
로그인이라는 사용자의 자원을 요청하는 행위가 왜 하는지를 생각해보자. 이 행위의 목적은 사용자 인증이다. 요청하는 컴퓨터가 사용자인지를 검증한다. 검증이 성공하였으면 인증이 완료되었다는 자원(세션이나 토큰 등)을 생성하여 반환한다. 즉 로그인이라는 과정은 사용자를 인증하고 인증이 완료되었다는 정보를 표현하는 자원을 생성하여 반환하는 과정이다. 여기서 인증이 완료 자원인 세션과 토큰값은 항상 일정하다는 보장이 없다. 즉 로그인 과정은 비멱등성의 성질을 가진다.
GET, PUT, DELETE, OPTIONS 메소드는 멱등성을 제공한다. 여려번 같은 자원을 요청, 수정, 삭제를 요청하고 결과를 반환한다. 이는 몇천번 시도해도 동일하다. 그래서 GET으로 로그인 요청을 수행하지 않는다. 즉 멱등성을 제공하는 GET메소드는 로그인 과정에 사용할 수 없다. 반면 POST 메소드는 멱등성이 없다. 같은 자원을 계속해서 생성을 요청하면 서버는 이들을 모두 다른 자원으로 보고 별다른 문제가 없다면 모두 성공으로 처리하여 같은 요청에 대해 서로 다른 자원을 생성한다. 즉 요청할 때마다 서로 다른 값을 제공하기 때문에 비멱등성의 성질을 가진다.
따라서 POST메소드를 쓰는 이유는 2가지로 알 수 있다.
- 보안적인 측면으로 사용자의 인증 정보를 어느정도 숨겨준다.
- 로그인의 행위는 비멱등성을 가지므로 이를 표현하는 유일한 메소드인 POST를 이용한다.
http 상태코드
클라이언트에서 요청한 내용을 서버가 처리한 결과를 의미한다. 코드마다 의미가 다르지만 100번째 자리를 통해 어떤 내용인지를 대략적으로 파악할 수 있다.
- 1xx: 단순 정보
- 2xx: 요청이 정상적으로 접수하였다.
- 200(OK): 요청이 성공적으로 수행되었으며 다음과 같은 데이터를 반환하였다.
- 204(No Content): 요청에 성공적으로 수행되었으며 별도의 데이터를 제공하지 않는다.
- 3xx: 요청을 수행하기 위해 특정 작업을 수행할 것을 요청
- 301(Move Permanently): 요청하는 자원의 위치가 영구적으로 변경되었다.
- 302(Found): 요청에 대한 자원의 위치가 임시로 변경되었다.
- 304(Not Modified): 요청에 대한 자원의 정보가 변경되지 않았다. 즉 클라이언트의 자원을 그대로 사용해도 무방하다.
- 307(Temporary Redirect): 요청에 대한 자원의 위치가 변경되었으며 이 요청을 보낸 메소드와 동일한 메소드를 사용하여 요청하여야 한다.
- 308(Permanent Redirect): 요청에 대한 자원이 Location 헤더에 명시된 URI로 영구적으로 변경되었다. 이 요청을 보낼 때 동일한 메소드를 사용하여 변경된 자원에 재요청을 해야한다.
- 4xx: 잘못된 요청
- 400(Bad Request): 잘못된 형식으로 자원을 요청하였다.
- 401(Unauthorized): 자원에 접근할 권한이 없다. 요청을 보낸 클라이언트를 식별할 수 없다.
- 403(Forbidden): 자원을 접근할 권한이 없다. 요청을 보낸 클라이언트를 식별 후 반환한다.
- 404(Not Found): 이 자원이 존재하지 않는다.
- 408(Request Timeout): 시간 초과로 인해 서버가 이 메세지를 수신받지 못했다.
- 409(Conflict): 요청된 자원이 서버의 상태와 맞지 않는다.
- 5xx: 요청은 전달 받았으나 처리과정에서 실패
- 500(Internal Server Error): 요청을 접수받았으나 이를 처리하는 방법을 모르겠다.
- 502(Bad Gateway): 요청을 처리하기 위해 게이트웨이가 잘못된 응답을 수신하였다.
- 503(Service Unavailable): 요청을 처리가 준비되지 않았다.
http의 상태코드는 대부분의 개발자들이 이해하고 있기 때문에 http프로토콜을 사용하지 않아도 이 상태코드를 쓰는 경우가 있다. 별도로 상태코드를 관리해야하는 문서도 만들 필요도 없고 개발자들간의 소통 오류가 발생할 가능성이 적어서 사용되곤 한다. 필자도 tcp소켓으로 통신서버를 만들 때 처리 결과를 반환할 때 사용할만한 내용이 없어서 http 상태코드로 표현한 경험이 있다.
http프로토콜 변천사
http프로토콜의 변천사를 간단히 요약하면 다음과 같다. 한줄로 요약한 만큼 일부 누락된 내용이 있을 수 있다.
- http1.0: http의 등장
- http1.1: RTT를 감소하자
- http2.0: 중복으로 송수신하는 데이터의 양을 줄이고 동시성을 높이자
- http3.0: 전송 계층의 프로토콜을 UDP로 교체하여 이전 프로토콜의 한계를 극복하자
http1.0: http의 등장
가장 원시적인 http프로토콜이다. Short-lived Connections이라는 가장 원시적이면서 http성질을 준수한 연결 수립을 수행한다.
이전에 0.9버전이 존재하긴 했으나 상용화되어 사용하기 시작한 것은 1.0버전이다.
Short-lived Connections
연결을 요청할 때마다 tcp세션을 맺고 해제하는 가장 원시적인 방법이다. 자원을 요청할 때마다 tcp세션을 맺고 해제한다.
http1.0의 문제점: RTT증가
http를 여러번 요청할 경우 tcp세션을 얻고 해제하는 시간 때문에 RTT가 증가한다. 예를 들어 html내에 이미지가 존재할 때 브라우저는 tcp세션연결을 2번이나 얻고 해제한다. 특히 같은 서버에서 데이터를 요청할 때는 같은 서버를 두번이나 세션을 얻고 해제하는 불필요한 작업을 한다.
- RTT(Round Trip Time): 패킷 왕복 시간의 약어, 통신이 시작하고 종료하는데 걸리는 시간
http1.1: RTT를 감소하자
RTT를 감소시키기 위해 3가지의 통신방법을 해결책을 제시하였다. 1개를 제외한 나머지 방법은 사용되진 않는다.
Persistent Connection
http1.1에서 사용하는 가장 대표적인 connection수립 방법이다. http1.1의 connection을 의미하면 해당 Connection을 이용해서 연결을 수립한다고 말할 정도로 사실상 기본 통신 방법이라고 할 정도로 많이 사용되는 방식이다.
이 방식은 http request header에 Connection의 값에 따라 데이터를 모두 수신해도 연결의 유지여부를 결정한다. 종류는 다음과 같다.
- close: 연결을 종료
- keep-alive: 연결을 유지
유지되는 기본 시간은 60초이며 설정에 따라 변경 가능하다.
이 방식은 2가지의 단점이 있다.(HTTP persistent connection. (2022.11.18) Wikipedia의 Disadvantages section 참고)
- 클라이언트가 세션 종료 과정 없이 종료되었어도 연결을 유지한다.
클라이언트가 기기가 갑자기 종료되거나, 네트워크문제 등 알 수 없는 이유로 세션 종료과정 없이 먼저 종료되었어도 서버는 이를 알 수 없어서 연결상태를 계속 유지한다. 물론 서버도 60초동안만 연결을 유지한다는 기본 설정이 있어서 어느정도 해결은 된다.
- race condition
클라이언트가 세션 종료 요청을 서버가 종료하는 시점과 동시에 받았을 경우에 race condition 현상이 발생할 수 있다. 이 문제가 발생하면 서버는 즉시 408(Request Timeout)상태코드를 반환한다.
408상태코드를 받으면 클라이언트는 처리되지 못한 데이터가 존재한다고 판단하여 새로운 세션을 맺고 데이터를 다시 보낸다. 즉 세션 종료라는 메세지를 보내기 위해 새로운 세션을 맺는 불필요한 일을 처리한다. 물론 이 일은 모든 http메소드에서 발생하지 않는다. 이 일이 발생하는 메소드는 멱등성 성질을 가진 메소드만 발생한다.
Http Pipelining
클라이언트가 필요한 자원들을 모두 요청하고 요청받은 순서대로 응답하는 방식이다. 이 방식은 자주 사용되지 않는다. 이 방식을 사용할려면 사용자가 요청해야 하는 모든 자원들을 알고 있어야 한다. 웹으로 따지자면 서버에 요청하기 전에 html자원, 이미지 자원, 폰트 자원등을 이미 알고있는 상태여야 한다.
pipelining을 이용하면 html의 로드율을 굉장히 높일 수 있다. 이유는 필요한 요청을 모두 하고 응답받기 때문에 html을 로드하는 과정에서 추가로 요청할 필요가 없기 때문이다. 다만 요청한 순서대로 응답을 주기 때문에 HOLB 문제를 가지고 있다.
http1.1의 문제점: HOLB(Head-of-line blocking) Problem
http1.1의 문제라고 적었지만 http1.0에도 해당되는 사항이다. 요약하자면 용량이 큰 데이터 1개가 수신중이라서 다른 데이터가 처리되지 못하는 현상이다. FIFO인 큐 자료구조를 사용할 때 발생하는 문제이다.
위의 그림은 10GB라는 데이터를 클라이언트에게 보내고 있는 중이라 크기가 작은 데이터인 50kb의 데이터들이 대기하는 그림이다.
http2: 중복으로 송수신하는 데이터의 양을 줄이고 동시성을 높이자
http2는 구글이 만든 SPDY프로토콜을 기반으로 만들어졌으며 http1.1를 확장해 만들어진 프로토콜이다. 해당 프로토콜은 기존의 http규칙을 따라가되 여러가지 방식들을 통해 http1.1보다 중복되어 송수신되는 데이터 양을 줄이고 여러개의 파일을 tcp connection추가 없이 동시에 보낼 수 있도록 한다.
http2가 SPDY프로토콜 기반인 만큼 아래에 설명하는 내용들은 구글 문서에서 가져온 내용들이 많다.
http2.0의 특징
- tcp connection을 1개만 이용하지만 Multiplexing으로 HOLB문제를 어느정도 해결한다.
- Http1.x의 규칙을 준수한다.
- 텍스트가 아닌 바이너리 형식으로 데이터를 송수신한다.
- 위의 요소들로 인해 http1.x보다 적은 레이턴시와 높은 동시성을 보장한다.
http2에 대해 설명하기 전에 http1.x에선 사용하지 않는 새로운 용어들이 있어 이에 대한 설명을 하고 시작하겠다.
Stream
- 클라이언트와 서버가 양방향으로 통신할 수 있는 논리적인 연결 통로
- 여러개의 stream을 쓰더라도 실제로 사용하는 tcp소켓은 1개이다.
- 각각의 stream들은 식별자로 구별할 수 있다.
- 우선 순위 트리에 의해 전송 순서가 제어된다.
Message
- 요청과 응답에 대한 원문 메세지를 의미한다.
- 메세지는 여러개의 Frame들로 이루어진다.
Frame
http2.0이 송수신되는 가장 작은 단위이며 바이너리 데이터로 구성되어 있다.
Frame은 2가지 종류가 있다.
- HEADERS: http의 header데이터. 요청, 응답에 대응되는 HEADERS를 모두 전송할 때까지 DATA를 보내지 않는다.(Hypertext Transfer Protocol Version 2 (HTTP/2) 8.1.3. Examples. (2015.05). RFC-7540 참고)
- DATA: http의 body역할, 요청, 응답으로 보내는 데이터
위의 그림처럼 데이터가 전송되는 양에 따라 여러개의 Frame으로 분리하여 보낼 수 있다.
http2.0과 http1.x는 다른 프로토콜인가
Frame의 설명에서 한가지 의문점이 있다. 바로 header와 body를 따로 보낼 수 있다는 것이다. 앞에서 나열한 지식으로 알 수 있는 것은 2가지이다.
- http2는 http1.x와 달리 텍스트가 아닌 바이너리 데이터인 Frame이란 단위로 소통한다.
- http2는 header도 body도 모두 Frame단위로 보낸다.
1번과 2번을 합치면 http2는 header와 body도 Frame단위로 보내기 때문에 이들을 따로 보낼 수 있다 는 사실을 알 수 있다. 이는 header와 body를 하나로 묶어서 전송하는 http1.x와는 다른 송수신 방식이다. 이 내용으로 http2가 http1.1과 이름만 같고 완전히 다른 새로운 프로토콜일 수도 있다는 의심을 할 수 있다. 그러나 아니다. http2는 http1.x를 확장한 프로토콜이다.
다음은 Introduction to HTTP/2 Design and technical goals. (last modified 2019.09.01). Google Developers 에서 발췌한 내용이다.
It is important to note that HTTP/2 is extending, not replacing, the previous HTTP standards. The application semantics of HTTP are the same, and no changes were made to the offered functionality or core concepts such as HTTP methods, status codes, URIs, and header fields. These changes were explicitly out of scope for the HTTP/2 effort. That said, while the high-level API remains the same, it is important to understand how the low-level changes address the performance limitations of the previous protocols.
위의 내용을 읽어보면 http2는 이전버전의 HTTP 규칙을 대체하지 않고 확장하는 형태임을 알 수 있다. 즉 http2는 이전버전의 메소드 url, 상태코드, 헤더필드의 내용을 변경하지 않는다. 이로 인하여 개발자가 사용하는 고수준의 http api는 변경하지 않고 저수준의 API들을 바꿔서 이전 프로토콜의 한계점을 돌파하고자 한 것이 http2이다. 즉 http1.x의 규칙은 준수한다.
Binary framing layer
http1.x의 규칙을 그대로 준수하기엔 송수신되는 데이터의 정보가 다르다. htp2는 바이너리 데이터이지만 http1.x는 텍스트 데이터이다. 예를 들자면 http1.x는 header와 body사이에 \r\n
가 구분자 역할을 수행한다. 반면 바이너리 데이터로 송수신하는 http2.0에서 http1.x에서 사용가는 텍스트 형식의 구분자를 그대로 사용할 수 있을리 없다. 즉 http2.0을 http1.x규칙 그대로 사용해서 데이터를 송수신하는 요청이나 응답을 전송 계층이 받으면 이해할 수 없다. 따라서 전송계층이 http2.0을 이해하기 위해 변경해주고 전송계층이 받아온 데이터를 http2.0에 맞게끔 다시 바꿔주는 작업이 필요하다. 이 작업을 binary framing layer가 수행한다
frame단위로 분해하고 여러개의 stream이 tcp세션을 공유하기 위해 전송 계층에 송수신할 데이터를 보내고 frame으로 받은 데이터를 http1.x규칙에 맞게끔 재조립해주는 계층이다.
이 계층의 존재로 http1.x의 규칙을 준수하면서 http1.x에선 제공할 수 없는 동시성과 적은 레이턴시를 제공받을 수 있다. 즉 http1.x를 사용하던 서비스를 http2.0에 맞게끔 별도로 변경해줄 필요는 없다. 물론 http2.0에 맞게끔 변경해주면 더 잘 활용할 수 있다. 그러나 완전히 실행이 안되는 것과 호환되어 실행이 된다는 것은 확실한 차이다.
이를 이용할려면 클라이언트와 서버 모두 binary framing layer가 필요하다는 단점이 있다고 생각할 수 있으나 아니다. Introduction to HTTP/2 Binary framing Layer. (last modified 2019.09.01). Google Developers 에선 http2를 제공하는 모든 프레임워크가 제공해주기 때문에 별도의 변경 사항을 느끼지 않고도 사용할 수 있다고 설명한다.
이제 3개의 용어에 대해 이해하였으니 http2.0의 3가지 기술을 설명하겠다.
1. multiplexing
- Origin: Introduction to HTTP/2 Streams, messages, and frames. (last modified 2019.09.01). Google Developers
tcp connection 1개를 통해 여러개의 stream을 만들어서 데이터를 송수신한다. 물리적인 연결은 1개이지만 논리적인 연결통로는 여러개를 만들어서 한쪽 통로가 큰 데이터를 송신중이라 막혀있을 때 다른 통로로 데이터를 보낼 수 있다. 이로 인해 HOLB 문제가 해결된다.
그러나 HOLB문제가 완전히 해결되진 않는다. 이유는 뒤에서 후술하겠다.
2. Header Compression
HPACK을 이용해 header의 메타데이터를 압축해 송수신하여 header를 압축(compression)하는 방법이다. JWT같이 헤더에 포함되어 전송할 데이터가 커지면서 header만 보내는 것도 데이터의 크기가 커졌기 때문에 header를 압축하여 송수신하는 데이터의 양을 줄여서 속도 향상을 노리고자 한다.
HPACK의 요소는 크게 2가지로 구성된다
1. Huffman coding
HPACK에서 문자형 리터럴을 표현할 때 사용한다. 문자열을 압축하는 알고리즘이며 문자열 빈도수를 기준으로 최소힙을 만들어서 압축한다.
2. indexing table
중복되는 데이터를 송수신하는 상황을 방지하기 위해 인덱싱 테이블을 이용한다. 클라이언트와 서버 모두 동일한 인덱싱 테이블을 가져서 이미 알고 있는 내용을 재전송할 경우 인덱싱 테이블의 키를 보내서 중복된 데이터의 송수신을 방지한다.
인덱싱 테이블은 2종류이며 다음과 같다.
- static table: http규칙에서 자주 사용되는 헤더의 키와 데이터들을 인덱싱한 테이블
- dynamic table: FIFO로 구성되어 있으며 static table에 존재하지 않는 header가 인덱싱되어 있다. header의 내용은 압축이 풀려있는 상태이다. dynamic table은 항상 새로운 데이터가 들어오고 나온다. 이때 중복 체크를 수행하지 않기에 dynamic table에 중복된 데이터가 존재할 수 있다. (HPACK: Header Compression for HTTP/2 section-2.3.2. (2015.05). RFC-7541 참고)
아래의 내용은 RFC-7541에 작성된 static table의 일부를 가져온 내용이다. 이를 보면 인덱싱 테이블은 Index, Header Name, Header Value 형식으로 구성되는 것을 알 수 있다. 이 내용들은 huffman coding 압축이 안된 상태로 저장되어 있다.
Index | Header Name | Header Value |
---|---|---|
1 | :authority | |
2 | :method | GET |
3 | :method | POST |
4 | :path | |
5 | :path | /index.html |
6 | :scheme | http |
7 | :scheme | https |
8 | :status | 200 |
9 | :status | 204 |
10 | :status | 206 |
11 | :status | 304 |
12 | :status | 400 |
13 | :status | 404 |
14 | :status | 500 |
15 | accept-charset | |
16 | accept-encoding | gzip, deflate |
17 | accept-language |
HPACK: Header Compression for HTTP/2 Appendix A. Static Table Definition. (2015.05). RFC-7541
3. Server Push
클라이언트가 요청하지 않는 자원을 보낼 수 있다. 가장 잘 알려진 예시가 폰트이다. 출력하는 html에서 특정 폰트를 사용한다면 이전의 http에서는 2가지 과정을 수행하여야 한다.
- html데이터를 받아온다.
- 폰트데이터를 받아올 동안 html데이터를 보여주지 않는다. 이때 로딩창을 써서 사용자에게 어떠한 처리작업을 수행중임을 알려준다.
- 폰트 데이터를 받아오면 html데이터를 보여준다.
이 과정은 사용자, 서비스 제공자측 둘 다 손해보는 과정이다. 사용자 입장에서는 폰트 데이터를 추가로 받기 위한 시간을 손해보고 서비스 제공자 입장에서는 사용자 1명에 대해 2번의 요청과 응답을 수행하여야 하므로 번거롭다.
Server Push는 이러한 점을 해결해준다. html페이지를 요청하면 서버가 이에 필요한 폰트정보까지 같이 제공하는 것이다. 클라리언트는 html페이지만 요청했지만 서버는 요청한 html페이지와 요청하지 않는 폰트 정보 를 같이 제공한다. 클라이언트는 폰트정보를 서버에 요청할 필요 없이 html페이지와 같이 받은 폰트를 사용하여 페이지 랜더링 시간을 줄일 수 있다. 이처럼 클라이언트가 요청하지 않는 자원을 서버가 제공해주는 것을 Server Push라고 한다.
http2.0문제점: 이 모든 것이 TCP의 제약조건에 걸린다
http2.0이 Multiplexing으로 여러개의 데이터를 동시에 보내주고 이들은 stream으로 구성된다. 이렇게 개선해도 불구하고 HOLB문제는 발생한다. 이유는 stream는 논리적인 연결이고 실제로 데이터를 송수신하는 역할은 tcp세션 1개만 담당하기 때문이다. 자세한 내용은 stream Priority를 보며 설명하겠다.
Stream Priority: 생성된 stream들이 모두 동시에 처리되진 않는다
우선순위 트리를 이용하여 stream들이 연결된 tcp세션에서 사용할 수 있는 할당량을 정한다.
이 tree는 왜 필요할까? 이유는 stream은 논리적인 통로이기 때문이다. stream을 만들 때 새로운 tcp 세션을 맺지 않는다. 기존에 만들어놓은 tcp 세션을 이용한다. 즉 stream으로 여러개의 통로를 만들어도 실제로 데이터를 송수신하는 물리적인 통로는 tcp 세션 1개뿐이다. 전체 stream들 중 누구의 stream을 먼저 처리할 지를 결정하는 기준이 우선순위 트리이다.
우선순위 트리는 빈 노드인 root 노드와 1이상 256이하의 가중치가 존재하는 child 노드로 이루지며 모든 stream이 우선순위가 아닌 root노드와 가장 가까운 depth가 1부터 차례대로 데이터를 보낸다. 만약 같은 depth에 여러개의 stream들이 있으면 각 노드의 비중만큼 tcp세션의 자원을 사용한다. 즉 형제노드들은 동시에 처리된다.
가중치에 따라 stream이 tcp세션에서 사용할 수 있는 양을 정할 수 있다. 공식은 다음과 같다.
비중=가중치/(형제노드의 모든 가중치의 합)
첫번째 트리는 A는 12/16, B는 4/16만큼 tcp세션에 할당되어 네트워크 처리를 수행한다.
두번째 트리의 의미는 D의 처리를 완료할 동안 C는 대기한다.
세번째 트리는 D의 처리를 완료한 후 C를 처리한 후에 A와 B를 12/16, 4/16만큼 tcp세션에 할당하여 네트워크 처리를 수행한다.
이처럼 수백개의 stream이 동시에 데이터를 보내는 것 처럼 보여도 이들은 별도의 tcp세션을 가지지 않으므로 tcp세션 1개라는 작은 자원에 수백개의 stream들이 몰린다.
세번째 트리를 보자. 만약 D가 처리하고 있는 데이터가 50GB이고 나머지 스트림이 처리하는 데이터가 50kb이면 D 때문에 C, A, B가 대기하는 HOLB문제가 발생된다.
http3: 전송 계층의 프로토콜을 UDP로 교체하여 이전 프로토콜의 한계를 극복하자
QUIC 프로토콜을 이용해서 http통신을 수행하는 방식이다. QUIC는 TCP가 아닌 UDP프로토콜을 이용한다. 이 점 때문에 HOLB문제를 완전히 해결되었다. tcp에서 해결하지 못했던 문제를 UDP에서 어떻게 해결했을까? tcp와 udp의 차이를 보며 설명하겠다.
1. TCP는
- 한번에 보낼 수 있는 패킷이 제한되어 있고 (MSS)
- 데이터 송수신을 위해 반드시 tcp 3 way-handshake로 연결을 수립해야 하고
- tcp 초기의 빈 header 공간은 제약조건의 발달로 인해 더이상 추가할 공간이 거의 없어 커스텀이 어렵고(Reserved 헤더)
- 클라이언트가 여러개의 데이터를 받기 위해 여러개의 소켓을 만들면 서버도 이에 대응하는 수의 소켓을 만들어야 하고
- 지금 사용하지 않는 header도 '규칙'이라는 이름 하에 남아있어 존재하지 않는 규칙의 삭제도 어렵다. (URG flag)
2. UDP는
- 한번에 보낼 수 있는 데이터가 제한되어 있지 않고
- src와 dst정보만 존재하여 header크기가 작고
- tcp에 비해 발전이 안되어서 커스텀하기 유리하고
- 3 way handshake같은 연결 수립 과정이 없어 모바일에서 유리하고
- 클라이언트와 서버는 연결 대수와 상관없이 1개의 udp소켓만 가지고 있어서 부하가 적고
- 과거에 비해 네트워크 상황이 안정되어 있어 데이터 손실율이 적다.
HOLB 문제를 완전히 해결한 이유는 소켓이다. tcp를 이용하여 데이터를 병렬로 보내는 방식은 데이터 개수만큼 tcp세션을 만들어야 한다. 반면 udp는 데이터를 병렬로 보낼 때 만들어놓은 1개의 udp세션만으로 데이터를 무작위로 섞어서 보내는 것으로 병렬로 데이터를 보낼 수 있다. 데이터를 무작위로 섞어서 보내도 수신측에서 무작위로 받은 데이터를 조립하는 커스텀 기능을 추가하여 인터넷이 안정적으로 연결되어 있다는 조건 하에 tcp에 준하는 신뢰도를 얻을 수 있다.
Reference
- HTTP persistent connection.(last modified Nov 1, 2022) Wikipedia
- HTTP pipelining.(last modified Nov 15, 2022) Wikipedia
- Connection management in HTTP/1.x.(last modified Sep 9, 2022) MDN Web Docs
- Ilya Grigorik & Surma. (last modified 2019.09.01). Introduction to HTTP/2. Google Developers
- HPACK: Header Compression for HTTP/2 Appendix A. (2015.05). RFC-7541
'CS' 카테고리의 다른 글
[자료구조] 해시맵 (0) | 2022.07.13 |
---|---|
[자료구조]BST, AVL트리 (0) | 2022.07.06 |
[자료구조] 트리 개론 (0) | 2022.07.04 |
[Web]CORS (1) | 2022.06.20 |
[GoF] 2. Factory (0) | 2022.03.22 |