HTTP/2.0

hyeok3011·2023년 10월 25일
2
post-thumbnail

HTTP/2.0에 대해서 다뤄보려고 한다.
HTTP/2.0의 구성이나 정보들과 HTTP/1.1에서 어떻게 개선이 된건지 왜 이렇게 개선이 됐을까에 대해서 조금 다뤄보려고 한다.

먼저 TPC, HTTP 글을 보고오면 더 좋을듯 하다.

HTTP/2 탄생 배경

먼저 HTTP는 단순하게 문서 전송을 위해 시작되었지만 시간이 지나며 웹상에서 상호작용하는 빈도수가 높아지고 실시간에 가까운 성능을 요구하는 환경에서는 성능적 한계에 부딪혔다.
Expire&ETag, 다수의 TCP커넥션, 리소스 인라이닝, concatenation srpiting, 도메인샤딩 등 다양한 최적화 방법등이 생겼다.
위 다 적용하기도 힘든 최적화 방법들을 모두 사용하면 충분할까? 근본적인 문제를 해결하면 되지 않을까?

SPDY

2009년도 중반에 구글에서 발표한 실험적인 프로토콜로서, HTTP 1.1의 잘 알려진 성능적 한계를 해결하여 웹 페이지의 로딩 레이턴시를 줄이는 것이 주된 목표였다.

스피디의 목표중 하나였던 페이지 로딩 시간(PLT)를 50%로 줄인다 라는 목표를 위해 기존의TCP커넥션을 좀 더 효율적으로 활용하는 방법을 택했다. 새로운 바이너리 프레이밍 계층을 도입함으로써 요청과 응답 멀티플렉싱, 우선순위 등을 활성화하고 불필요한 네트워크 레이턴시를 최소화하거나 아예 제거하도록 하였다.

결과는 매우 성공적이였고 HTTP 워킹 그룹(HTTP-WG)이 스피디 프로토콜을 본보기로 삼아 그 장점을 공식 표준화 하고 개발 착수에 들어갔다.

HTTP/2.0 핵심 디자인

  • TCP를 사용하여, 대부분의 경우 HTTP 1.1보다 대폭적으로 사용자단의 레이턴시를 개선해야 한다.
  • HTTP의 문제점인 HOL 블로킹(head of line blocking)을 해결해야 한다.
  • 병렬화를 위하여 서버에 다수의 커넥션을 요구하지 않고, 특히 혼잡 제어에 있어서 TCP 사용 효율을 높여야 한다.
  • 기존의 스펙 문서를 활용하여 HTTP 메서드, 상태 코드, URI, 헤더 필드를 비롯한 HTTP 1.1의 기본 틀은 유지해야 한다.
  • HTTP 2.0이 HTTP 1.x과 어떻게 상호작용을 하는지(특히 중계자 내에서) 명확하게 정의해야 한다.
  • 새롭게 확장되는 영역을 명확하게 구분 짓고 각각의 적절한 사용방법에 대한 정책을 마련해야 한다

위 내용을 다시보면 1.x표준을 확장하고자 하는것이고 HTTP의 애플리케이션 시맨틱이나 HTTP메서드, 상태코드, URI, HEADER field같은 기존 기능과 핵심 콘셉트에는 바뀌는점은 없다.
그렇다면 뭐가 바뀌었길래 2.0으로 버접을 했을까?
그것은 새 바이너리 프레이밍 계층을 추가한 것이다.
클라이언트와 서버가 데이터를 주고받는 방식이 바뀌어 1.x서버와 클라이언트에는 호환되지 않는다.

바이너리 프레이밍 계층

HTTP/2.0의 성능 개선의 핵심은 바이너리 프레이밍 계층이다.

바이너리 프레이밍 계층은 소켓 인터페이스와 애플리케이션에게 노출되어 있는 상위 계층 HTTP API 사이를 잇는 새 메커니즘이며 바이너리 인코딩 및 디코딩을 담당한다.
메서드나 헤더 같은 HTTP 시맨틱은 변하지 않았으나 HTTP/2.0을 사용하려면 바이너리 프레이밍 계층이 존재해야 한다.

위 그림에서 보이듯 newline으로 구분되던 Plain Text 인코딩 방식인 HTTP/1.x와 달리 HTTP/2.0에서는 모두 바이너리 형식의 더 작은 메시지와 프레임으로 나뉜다.

스트림, 메시지, 프레임

바이너리 프레이밍이 도입되 서버와 클라이언트 데이터 교환 방식이 바뀌었다.

  • 스트림
    생성된 커넥션 존재하는 가상 채널로 양방향으로 메시지를 전달한다. 각 스트림은 고유의 정수 식별자를 가진다.

  • 메시지
    요청,응답 같은 논리적 HTTP메시지이며 하나 이상의 프레임을 포함한다.

  • 프레임
    HTTP/2.0 커뮤니케이션에서 사용되는 가장 작은 단위로 각 프레임에는 어느 스트림에 속해있는지 지정하는 프레임 헤더를 포함하고 HTTP Header, 페이로드 같은 데이터를 운반한다.

HTTP/2.0은 양방향 스트림을 운반할 수 있는 커넥션에서 이루어 진다.
각 스트림은 메시지를 이용하여 소통하고 그 메시지는 하나 이상의 프레임으로 이루어져 있다.

멀티 플렉싱

HTTP/1.1에서는 한 번에 하나의 응답만을 전달하는 전달 모델때문에 성능 개선을 위해 다수의 TCP연결을 이용했다.

HTTP/2.0 바이너리 프레이밍은 여러개의 HTTP요청과 응답을 독립적인 메시지와 프레임으로 쪼개어 전달한 후 수신 측에서 재구성 하기 때문에 멀티 플렉싱이 가능하다.

위 그림에서 보이듯 클라이언트는 Stream5에 대한 요청을 보내면서도 stream 1,3에 대한 응답도 함께 받고있다.

하나의 TCP커넥션을 사용하면서 다수의 요청을 응답을 병렬도 삽입하더라도 서로 블로킹 되지 않는다.
멀티플렉싱 덕분에 HTTP의 HOL블로킹 문제 또한 해결되었다. (하지만 TCP의 HOL는 해결되지 못했다.)

하나의 커넥션

바이너리 프레이밍에 의한 멀티플렉싱 기능은 더이상 HTTP/1.x처럼 다수의 TCP커넥션을 사용할 필요가 없어젔다.
클라이언트, 서버 모두 오직 하나의 커넥션만 사용하면 된다.

실험 결과에 따르면 클라이언트에서 적은 수의 커넥션을 사용하는 것이 레이턴시에 지속적으로 도움이 된다는 것을 알 수 있었다. HTTP 2.0에서 전송된 전체 패킷 수는 HTTP보다 40% 가량 적었다. 서버가 많은 동시 커넥션을 다루게 되면 확장성의 문제로 발전할 수가 있는데, HTTP 2.0이 이러한 부담을 줄이게 되었다.
-HTTP/2.0Draft 2

단일 커넥션이 TCP사용에 좋은이유

  • 다수의 커넥션을 사용한다는 뜻은 스레드를 의미한다. 스레드는 메모리 등 컴퓨팅 자원을 소모하게 된다.
  • TCP는 슬로우 스타트로 작은 윈도우 사이즈로 시작되어 통신 릴레이가 이어질수록 윈도 사이즈가 커진다.
  • 만약 패킷 손실이 일어나더라도 빠르게 손실 복구가 된다.

HTTP/2.0에서 HTTP/1.x처럼 다수의 커넥션을 사용하게 된다면 TCP의 처리량을 최대한 활용하지 못할것이고 헤더 압축과 우선순위의 기능 효과가 떨어지게 된다.


하지만 하나의 커넥션을 사용하면 단점도 존재한다.

단점

  • 위에서 말했든이 TCP레벨에서의 HOL블로킹은 존재한다. 1번 스트림이 문제가 생기면 2번 스트림에 영향을 줄수가 있다.
  • 패킷 손실이 일어났을때 TCP윈도 사이즈가 줄어 전체 처리량이 감소한다.
    위 단점들은 TCP와 관련이 있는데 TCP글도 읽어보면 좋을듯 하다.

우선순위

HTTP/2.0에서는 메시지의 우선순위를 정하여 애플리케이션의 성능을 최적화 할 수 있다.

각 스트림은 31비트의 우선순위 값을 배정받을 수 있다.

  • 0은 가장 높은 순위의 스트림을 나타낸다.
  • 2^31 - 1은 가장 낮은 순위의 스트림을 나타낸다.
    우선순위 값을 사용하여 스트림, 메시지, 프레임을 최적의 순서로 처리하기 위한 전략을 세울 수 있다.

예를들어 DOM을 구성하는데에 CSS와 CSSOM을 구성하기 위해 꼭 필요하다. 그러나 DOM과 CSSOM 모두 자바스크립트 리소스가 없으면 블로킹 되어 구축이 불가능하다.
그리고 이미지와 같은 나머지 리소스들은 낮은 우선순위를 가지게 된다.

서버 푸시

클라이언트가 요청을 하나만 보내도 서버는 응답을 여러개 보낼 수 있다.

이제 html, css, js파일 등을 각각 요청하지 않고 한번의 요청으로 모두 받을 수 있다.

만약 서버 푸시 기능을 사용하지 않고 기존의 HTTP/1.x를 사용한다면 리소스 인라이닝 최적화를 진행하거나 css, .js를 각각 요청 응답으로 받아야 할것이다.

헤더 압축

HTTP/2.0에서는 헤더 메타데이터를 hpack압축 메커니즘을 사용한다.

  • 각 요청과 응답마다 같은 데이터를 재전송하는 대신에, HTTP 2.0은 클라이언트와 서버 양측에서 이전에 보낸 key-value 쌍을 저장한 헤더 테이블을 사용한다.
  • 헤더 테이블은 HTTP 2.0 커넥션이 살아 있는 동안 계속 존재하며, 클라이언트와 서버 양쪽에서 점진적으로 업데이트된다.
  • 각 신규 헤더 key-value 쌍은 기존 테이블에 추가되거나 테이블 안에 있는 이전 값을 대체한다.

위 사진을 보면 두번째 요청에서는 :path만 전송되었다.
2.0은 중복된 헤더 데이터를 전송하지 않으므로 매 요청마다 오버헤드를 대폭 줄일 수 있다.
커넥션이 살아있는 동안 거의 바뀌지않는 user-agent, accept-header등은 단 한번만 전송될것이다.

HTTP/1.x에서는 중복되는 메타 데이터들을 중복으로 보내고 쿠키정보들까지 포함된다면 배보다 배꼽이 더 큰 상황이 많이 발생했다. 이로인한 오버헤드들또한 존재하였지만 HTTP/2에서 해결된 모습을 보인다.




마치며

함께 읽으면 좋겠다고 생각되는 자료

참고한 내용

  • HTTP2 wiki
  • 네트워킹과 웹성능 최적화 기법 - 책
profile
뇌가 디스크가 아니라는 사실을 깨달아 버린 사람

0개의 댓글