HTTP/1.2가 아니라 HTTP/2인 이유?
가장 큰 변화 : 0/1의 binary framing layer
1.1은 유니코드나 아스키코드로 인간이 읽을 수 있는 형태였지만, HTTP2는 Binary Framing layer가 있어서 0/1의 형태로 정보를 주고받게 한다.
HTTP1.1은 request message, response message가 있었으나, HTTP2는 request, response message 안에 frame이 들어간다. frame은 0/1의 bit level 형태로 만들어진다.
따라서 전송 방식이 아예 바뀌었으므로 1.2가 아니라 2라고 한다!
Binary Framing Layer
HTTP2에서도 HTTP1의 내용대로 통신이 가능하다. 헤더 내용을 헤더 프레임으로, 데이터는 데이터 프레임으로 보낸다.
HTTP/2 기본 용어들
- 메시지 : HTTP1에서는 하나의 요청 메시지, 하나의 응답 메시지였다면, HTTP2에서는 헤더와 데이터 영역으로로 나누고, 각 영역도 여러 개의 프레임(일정한 크기)으로 하나의 메시지가 전달된다.
- 프레임 : HTTP2에서 통신의 최소 단위. 각 프레임마다 하나의 프레임 헤더를 포함하며, 이 프레임 헤더는 프레임이 속하는 스트림을 식별한다.
- 스트림 : 구성된 연결 내에서 전달되는 바이트의 양방향 흐름. 하나 이상의 메시지가 전달될 수 있다.(작은 단위로는 하나의 요청과 하나의 응답, 넓은 의미로는 여러 개의 요청과 응답일 수 있다)
기본 용어들 관 관계?
하나의 TCP 연결에 의해 전달되는데, 이때 여러 개의 스트림이 전달될 수 있다. 각 스트림 내에서도 여러개의 프레임이 동시에 전달되므로 하나가 못 가더라도 다른 것들과 독립적으로 작용할 수 있다.
또한 각 스트림에는 양방향 메시지 전달에 사용되는 고유 식별자와 우선순위 정보가 있고, 각 메시지는 하나의 논리적 HTTP메시지(요청 또는 응답)이며 하나 이상의 프레임으로 구성된다.
프레임은 통신의 최소 단위로서 특정 유형의 데이터를 전달한다. 다른 스트림들의 프레임을 인터리빙한 다음 각 프레임의 헤더에 삽입된 스트림 식별자를 통해 이 프레임을 다시 조립가능하다.
이미지 출처 : https://web.dev/performance-http2/
헤더와 데이터는 왜 쪼갰나?
헤더를 보면 어떤 정보가 올 지 알 수 있다. 따라서 헤더가 먼저 오면 나중의 데이터 작업에 대해 미리 준비할 수 있으므로 유용할 수 있다.
HTTP2 특징
요청 응답 및 다중화
HTTP 1.1의 문제 다시 보기
HTTP/1.x에서 클라이언트가 여러 병렬 요청을 수행하려고 할 때, 여러 TCP 연결을 사용해야 한다.
이는 연결 당 하나의 요청-응답이 가능하게 한다. 이는 Head Of Line Blokcing을 일부 완화할 수 있지만, TCP사용을 위해 CPU, memory를 소모하게 된다. 이는 서버 부하를 ...
그래서 어떻게 해결하는데? : 바이너리 프레이밍 계층의 다중화 기능
- 전체 요청 및 응답 다중화를 지원한다. (다중화 : 장애가 발생해도 예비 운용장비로 시스템의 기능을 계속할 수 있도록 하는 것) 그러니까 하나의 line으로 송수신하는 게 아니라는 말.
- 클라이언트와 서버가 HTTP 메시지를 독립된 프레임으로 세분화하고, 이 프레임을 인터리빙 한 다음, 다른 쪽에서 다시 조립한다.(인터리빙 : 각 채널의 신호를 하나씩 순차적으로 추출하고는, 이들을 일렬로(차례대로) 직렬화시키며(끼워가며), 다중화하는 과정, 출처 : http://www.ktword.co.kr/test/view/view.php?m_temp1=1061)
- 클라이언트는 데이터 프레임(스트림 5)을 서버로 전송 중인 반면, 서버는 스트림1과 스트림 3의 인터리빙된 프레임 시퀀스를 클라이언트로 전송한다. 따라서 3개의 병렬 스트림이 존재한다.
- 다중화의 의미 : 서버가 요청 받은 순서대로 응답 보내지 않아도 된다!!
다중화의 효과?
- 여러 요청을 하나도 차단하지 않고 병렬로 인터리빙 가능
- 여러 응다을 하나도 차단하지 않고 병렬로 인터리빙 가능
- 단일 연결이지만 여러 스트림을 활용해서 여러 요청과 응답을 병렬 전달 가능
- 불필요한 HTTP/1.x 임시 방편들(연결된 파일, 이미지 스트라이프, 도메인 분할 등) 제거
- 불필요한 지연 시간을 제거하고 가용 네트워크 용량의 활용도를 개선하여 페이지 로드 시간 줄임
결론적으로, HTTP/1.x의 HOL차단 문제를 해결하고 하나의 연결로 요청과 응답의 병렬 처리가 가능해진다. 이는 어플리케이션이 빠르고 단순해지고 배포 비용이 절감하는 효과를 낳는다.
스트림 우선 순위 지정
- 우선순위는 스트림 단위로 처리한다.
우선순위가 중요한 애들을 고를 수 있는 스킴이 지원되는데, 이는 각 스트림에 연관된 가중치와 종속성을 갖도록 하여 나타낸다.
- 종속성이라는 것은 상하관계를 의미한다. super를 먼저 고려, sub를 나중에 고려하도록 한다.
우선순위 결정은 클라이언트가 정한다. 웹 브라우저가 서버가 준 정보를 보고 요청을 보낸다. 따라서 클라이언트가 우선순위 트리를 전달한다는 것은, 서버가 이미 클라이언트에게 무엇이 더 중요한지 알려준 것이다.
서버가 이 정보를 가지고 스케쥴링한다. cpu, 메모리 및 기타 리소스 할당으로 제어함으로써 스트림 처리의 우선순위를 지정한다.
응답 데이터가 있는경우, 서버는 우선순위가 높은 응답이 클라이언트에게 최적으로 전달되도록 대역폭을 할당.
우선순위 지원 동작 원리?
어떻게 우선순위를 부여할까?
- 스트림 종속성
자신 보다 우선 순위가 높은 또 다른 스트림의 고유 식별자를 상위 요소로 참조하는 방식으로 선언된다.
이 식별자가 생략되면 스트림이 '루트 스트림'에 종속된다.
스트림 종속성 선언은 가능하면 상위 요소 스트림이 종속된 스트림보다 리소스가 먼저 할당되어야 함을 나타낸다.
- 스트림 자원(리소스) 할당 방법?
동일한 상위요소를 공유하는 스트림들은 각자의 가중치에 비례하여 리소스가 할당되어야 한다.
예를 들어 스트림1의 가중치가 12이고 그 동위 요소 스트림2의 가중치가 4인 경우, 이들 스트림이 각각 수신해야 하는 리소스의 비율은
- 모든 가중치를 더하고 ( 12+ 4 = 16)
- 각 스트림 가중치를 총 가중치로 나눈다(1번 = 12/16, 2번= 4/16)
따라서 스트림1은 가용 리소스의 3/4를 받고, 스트림2는 1/4를 수신해야 하며, 스트림2는 스트림1에 할당된 리소스의 1/3을 수신해야 한다. 즉, 1이 3개를 보내는 동안 2는 1개를 보내야 하는 것이다.
우선 순위 동작 예시?
a는 12, b는 4. 종속적으로 그리면 d아래 c. 상하관계인 경우 숫자는 의미 없음.
d > c > a : b = 3:1
우선 순위 지원 효과?
- 다양한 종속성 및 가중치와 여러 가지 리소스 유형이 존재하는 브라우저에서 그 성능 개선에 중요하다.
- 게다가 http2 프로토콜은 클라이언트가 기본 설정을 언제든지 업데이트 할 수 있도록 허용하므로 브라우저의 성능이 더욱 최적화 된다.
- 즉 사용자 상호작용과 기타 신호에 응답하여 종속성을 변경하고 가중치를 재할당할 수 있고, 이는 성능 최적화에 도움이 된다.(flexible)
우선 순위 주의 사항?
- 스트림의 종속성과 가중치는 전송 기본 설정을 표현하는 것이지 요구사항을 표현하는 것은 아니다. 따라서 특정한 처리나 전송 순서를 반드시 보장하는 것은 아니다.
- 즉 클라이언트는 스트림 우선순위 지정을 사용하여 특정 순서로 스트림을 처리하도록 서버에게 강요할 수 없다.
- 이것이 이상하게 보일 수 있지만, 우선순위가 높은 리소스가 차단된 경우 우선순위가 낮은 리소스에서 서버 진행이 차단되는 것이 원하지 않으므로 바람직한 동작이다.
(특정 순서대로 스트림을 처리하게 되면 앞의 것이 처리되지 못하면 뒤의 스트림이 처리되지 못하므로 또 BOL blocking이 발생하기 때문이다)
하나의 TCP 연결
HTTP/2의 단일 TCP 연결을 통한 client-server 통신
- http1.1처럼 여러개 tcp만들지 마세용
- http1.1에서는 연결을 설정하고 해지하는 타이밍이 url창에 엔터키를 치는 때였다면, http2에서는 클라이언트 측에서 처음으로 웹 페이지를 보는 시점부터 연결이 되어서 그 이후로는 웹 페이지를 다시 열더라도 첫 연결을 유지한다.
- 연결 수가 적다는 것은 https 배포의 성능을 개선하는 데에 특히 중요하다. 연결 수가 적으면 비싼 TLS 핸드셰이크가 줄어들고, 세션 재사용이 더 향상되며 필요한 클라이언트 및 서버 리소스가 감소한다.
HTTP/2의 단일 TCP 연결 사용시 성능 개선 사항
- HTTP 전송은 수명이 짧고 폭주하는 반면(요청에 비해 응답이 매우 큼) TCP는 수명이 긴 대량 데이터 전송에 최적화 되어있다.
- HTTP/2에서는 동일한 연결을 재사용하여 각 TCP 연결을 더 효율적으로 사용할 수 있으며 또한 전반적인 프로토콜 오버헤드(즉 더 이상 HTTP req/res 들이 독립적이지 않다. 전후반에 상호 연관성. 앞 메시지 중 뒷 메시지에 중복되는 애들은 뒤에서 보내지 않음 : 압축)를 대폭 줄일 수 있다.
HTTP에서 압축이 가능한 이유? 압축에서 앞뒤관계가 성립되도록 한 단일 TCP연결에 대해 말해야 한다.
- TCP연결이 줄어든다는 것은 메모리와 처리량이 줄어든다.(cpu와 memory 절약, 서버 뿐 아니라 클라이언트, 프록시 서버도 마찬가지)
- 그 결과 전체 운영 비용이 절감(서버)되고 네트워크 활용도(지연 줄임)와 용량이 개선된다.
- 따라서 네트워크 지연 시간이 줄어들 뿐만 아니라 처리량이 개선되고 운영 비용이 줄어든다.
흐름 제어(다시보기)
일반적인 흐름 제어 개념
- 수신단에서, 송신단의 데이터가 불필요하거나 처리가 불가능한 경우, 수신단에 부담을 주는 것을 막을 수 있다.
- 예를 들어
수신단(클라이언트)이 사용 중이거나, 과부하 상태이거나, 특정 스트림에 일정 크기의 리소스만 할당하려는 다음과 같은 경우 등이다.
클라이언트가 높은 우선순위로 대용량 동영상 스트림을 요청했지만, 사용자가 이 동영상을 일시 중지하여 이제 클라이언트가 불필요한 데이터 가져오기와 버퍼링을 막기 위해 서버로부터의 동영상 전달을 일시 중지하거나 차단하려고 하는 경우 : stop and go
프록시 서버의 다운스트림(다운 받는 것) 연결은 빠르고 업스트림 연결은 느린 경우, 프록시 서버가 업스트림 속도에 맞게 다운스트림의 데이터 전달 속도를 조절하여 리소스 사용량을 제어하는 경우 등 : late control
HTTP/2 흐름 제어 특징
-
흐름 제어를 구현하기 위한 특정 알고리즘을 지정하지 않는다. 그냥 보내라, 많이보내라 .. 등등을 설정
- 그 대신 간단한 빌딩 블록을 제공하고 그 구현을 클라이언트와 서버에 넘긴다.
- 그러면 클라이언트와 서버가 이 빌딩 블록을 사용하여 사용자 설정 전략을 구현하여 리소스 사용과 할당을 제어한다.
-
웹어플리케이션의 실제 성능과 측정된 성능을 모두 개선할 수 있는 새로운 전달 기능도 구현하도록 지원한다.
- 예를 들어, 어플리케이션 계층 흐름 제어에서는 브라우저가 특정 리소스의 일부만ㅇ르 가져온 후, 스트림 제어 창을 0으로 줄여서 가져오기를 보류한 다음, 나중에 가져오기를 재개할 수 있도록 허용한다.
- 즉, 브라우저가 특정 이미지의 미리보기나 최초 스캔을 가져와서 표시할 수 있고, 우선순위가 더 높은 다른 가져오기 동작을 진행할 수 있으며, 더 많은 주요 리소스가 로딩된 후에 가져오기를 재개할 수 있다.
HTTP/2 흐름 제어 동작 원리
서버 푸시
서버 푸시 기능 개요
- http1.1에서 불가능한 기능으로, 서버는 원래 요청에 응답하여 동작하는데, 클라이언트가 명시적으로 요청하지 않아도 서버가 추가적인 리소스를 클라이언트에게 푸시할 수 있다.
- 클라이언트가 필요로 할 것을 서버가 알아서 내려주는 기능
- 예를 들면 광고를 계속 다른 걸로 push 가능
- stream2, 4는 promise로 네가 언젠가 필요로 할 것이니 내가 언젠가 너에게 보낼 것을 약속해~
서버 푸시 기능의 필요성
- 웹 기반의 어플리케이션은 많은 자원으로 구성되는데, 미리 서버가 다음 단계에 필요한 정보를 내린다면, 사용자 입장에서 반응 속도가 빠르게 보인다.
- 사실, 만약 css, js 또는 다른 ..?
그런데 서버 푸쉬를 하게 되면 css, js를 하나에 다 때려박지 않고 별도의 파일로 내보낼 수 있으므로 관리도 편하고 유지보수도 편하게 될 것이다.
(그럼 다시 mpa로 가나..?)
서버 푸시 동작
- 결국 서버가 미리 내릴 스트림을 판단한다. 이를 서버가 클라이언트에게 사전 공지 한다.(push_promise frame을 통해)
- 클라이언트 입장에서 이미 가지고 있다면 받을 필요 없으므로 서버에게 받을 것을 미리 판단한다.
- 이 push_promise frame은 http 헤더 정보를 가지고 있다.
- 클라이언트가 서버의 푸쉬를 거부할 수 있다. (rst_stream frame을 통해서 )
- 클라이언트가 서버의 푸쉬를 받는다.
- 클라이언트가 받는 것을 조절 할 수도 있다. 동시에 몇 개의 push stream을 열지 클라이언트가 제한할 수 있다. 또한 flow control을 활용해 속도도 조절 가능하다.
- 이러한 선호는 SETTINGS frame을 통해 조절된다.
-
서버가 푸쉬하는 것도 결국 스트림이기 때문에 multiplexed, prioritized, processed by client된다.
-
http2는 논리적 연결이 있지만, 하나의 tcp링크에 의해 주고받아지고, 보안은 tcp level에서 한다. 모두 하나의 tcp 보안 규칙을 따르고 있다.
-
push기능은 좋으나 이를 잘 짤 수 있는 알고리즘 만드는 사람이 잘 없음 ..
헤더 압축
각 HTTP transfer 들은 전송될 자원과 이것의 특성을 담은 헤드 한 세트를 보낸다.
- http1에서는 이러한 메타데이터는 항상 plain text로 보내졌고, 어디에서든지 500-800 바이트의 오버헤드를 안고 매 전송마다 보내졌다. 그리고 때때로 http 쿠키가 사용될 때 킬로바이트 이상이 전송되었다.
- http2에서는 오버헫를 줄이고 성능을 개선하기 위해, http2는 요청과 응답 헤더의 메타데이터를 HPACK 압축을 사용해서 압축했다.
HTTP/2의 HPACK compression foramt
- 어떻게 압축하냐? Huffman code를 활용한다.
- 차이값만 전달하는 코딩이다.
- 바뀐 내용만 전달한다.
- 이는 클라이언트와 서버 모두가 유지하고 업데이트 하도록 요청한다.
- http2는 req-res가 각각 독립적이지 않고, 하나의 tcp연결로 되어있기 때문에 앞/뒤를 가정할 수 있다. (stream) 따라서 앞쪽의 헤더는 뒷쪽의 헤더 입장에서 안바뀌었으면 다시 안보낸다.
Huffman coding
- 과거와 현재의 차이 기반 압축
- 지금 전달할 헤더가 전에 보낸 거랑 같다면 안보낸다.
- 달라진 부분만 보낸다.
HTTP2 헤더 압축 예제
-
같은 스트림이 아니어도 압축 된다.
-
req1, req2가 각각 stream1, 3일때 다 같고 path정보만 다르면 뒤의 req2의 헤더는 path 정보만 전송한다.
-
2번째 예제
헤더에 중복값이 존재하는 경우 header table개념을 사용하여 중복 헤더를 검출하고, 중복된 헤더는 index값만 전송한다. 중복되지 않은 header 정보의 값은 huffman으로 인코딩하여 전송한다.
기능적으로는 static table, dynamic table 두개를 사용한다.
바뀌는 테이블은 뭐고, 안바뀌는 테이블은 뭔가?
-
static table : 현재 보내지는 메시지와 상관없이 미리 만들어져 있는 것(자주 쓰여서 미리 만들어 둔 것)
-
dynamic table : 현재 tcp 연결로 서비스 할 때, 그 시작 시점부터 만들어지는 table
-
단점은 processing power가 늘어났다.
-
암호화도 하니까 성능이 줄어든다..
-
원래 HACK은 SPDY의 zlib에서부터 왔다. 그런데 해킹당해서 지금은 HACK쓴다..