Why use HTTP/2 over HTTP/1.x?

Jongho Jeon
Jongho’s Tech Blog
10 min readMay 8, 2022

--

naver.com network capture (HTTP/2)

그 이유와 HTTP/1.x 대비 차이를 아시나요? 최근에 꽤 유명한 웹사이트들은 HTTP/2를 많이 사용하고 있습니다. HTTP/2에서 개선된 HTTP/1.x의 성능상 문제점들과 그 외 기능들에 대해 간단히 알아봤습니다.

본 문서는 문서 하단의 reference들의 내용을 참고하여 정리한 것입니다.

HTTP/2 usage

의외로 오늘날 많은 웹사이트들이 HTTP/2를 사용하고 있다. google.com, naver.com 등 유명한 웹사이트들은 이미 HTTP/2를 사용하고 있다. Vercel도 HTTP/2를 사용하고, gRPC도 HTTP/2를 기반으로 하고 있다.

여전히 HTTP/1.1을 사용하고 있는 서비스들도 많지만 HTTP/2는 공부해볼만한 충분한 가치가 충분히 있다고 생각했다. 자신의 서비스가 언제 HTTP/2를 도입하여야 하는지 어떤 문제를 해결할 때 사용해야 하는지 판단하는 데에 도움을 줄 것이다.

많은 모던 웹 브라우저들과 AWS ELB, istio 등도 HTTP/2를 이미 지원하고 있기 때문에 일반적으로 사용하는 데에 큰 어려움은 없을 것이다.

HTTP/1.x performance issues

RTT overhead (Round-trip time)

HTTP/1과 HTTP/1.1의 차이부터 알아보려 한다.

HTTP 요청은 TCP를 기반으로 한다. 즉, HTTP 요청을 보내려면 TCP 커넥션을 맺어야 한다. HTTP 요청은 2 RTT로 이루어져 있다고 할 수 있다.

Establish TCP connection→ HTTP request/response

HTTP 요청을 100번 보내면, 200 RTT가 필요하다. 요청이 많을수록, 네트워크 통신 품질이 낮을수록 체감 성능이 낮아지게 된다.

이를 개선하기 위해, 많은 웹사이트와 웹브라우저에서는 비공식적으로 Keep-alive를 지원하였음. Keep-alive가 HTTP 헤더에 명시될 경우 커넥션을 유지하여 TCP connection overhead를 줄이는 것. (200 RTT → 1+100 RTT로 감소한다)

그 후 공식적으로 HTTP/1.1에서는 이를 개선하기 위해 persistent connection 기능이 추가됨. 즉, 기본적으로 커넥션이 영구적인 것으로 간주함.

실제로는, 너무 오래 유지되는 커넥션은 서버의 리소스를 많이 낭비하기 때문에 connection timeout 시간동안만 유지함. Apache httpd의 timeout은 15~5초임

Parallel connection

HTTP request들을 여러 개의 커넥션을 사용하여 병렬적으로 보낼 수도 있다

웹 브라우저에서 6개의 커넥션을 사용한다고 가정하면, RTT를 약 1/6로 감소시킬 수 있다.

HTTP/1.1에서 소개된 HTTP pipelining이라는 기능을 사용할 수도 있는데, 하나의 connection에서 여러 개의 HTTP request를 병렬적으로 보낼 수 있는 기능이다. 하지만 실제로는 많이 사용되지 않는 듯 하다. 중간의 proxy server들이 이를 제대로 처리해주지 않는다는 듯 하다. (자세히 찾아보진 않았다)

HOL issue (Head Of Line blocking)

persistent connection 및 parallel connection을 통해 어느 정도 HTTP 병렬 요청 문제를 개선하였음에도 불구하고 HOL issue가 여전히 존재한다.

web browser connection을 모두 소진한 후의 요청들은 이전 요청들이 완료될 때까지 한다. 웹페이지 내에서 수십 개의 HTTP request를 전송할 경우 HOL issue를 겪게 된다. html, js, css, png, json 등 다양한 리소스들을 요청하는 현대 웹사이트 특성상 자주 겪게되는 이슈다.

이 문제(HTTP layer의 HOL issue)는 HTTP/2의 request multiplexing을 통해 해결될 수 있다.

Too big header size

의외로 HTTP 패킷 중 header의 크기가 크다. 그 중에서도 Cookie가 특히 큰데, slideshare 웹사이트의 요청을 확인해보니 header size만 1.3KB였다. 그 중 Cookie가 806 B였다. 참고로 HTTP compression은 body에만 적용된다.

HTTP/2 features

Multiplexed streams

Multiplexed streams를 통해 위에서 언급한 HTTP HOL issue를 해결할 수 있다. HTTP/2 stream에 개념에 대해서 간단히 알아보자.

HTTP/2의 context에서 Stream이란 HTTP/2 connection 내에 있는 독립적인 양방향 프레임 연속(시퀀스)이다. Frame은 client와 server 사이에서 교환되는 단위(unit)을 의미한다.

HTTP/2 streams and multiplexing

Stream의 몇 가지 중요 특징들을 살펴보면, 하나의 HTTP/2 connection은 여러 개의 병렬 “open” stream들을 가질 수 있다.

Stream 내에서 프레임들의 전송 순서는 중요하며, 특히 HEADER 프레임과 DATA 프레임의 순서가 중요하다. 프레임들은 수신 순서대로 처리된다. 각각의 스트림은 int로 식별된다.

쉽게 말하면, 하나의 HTTP/2 connection을 사용하면서 각각의 HTTP request들은 각각 하나의 stream이 되어 frame을 교환한다. 여러 개의 stream들이 하나의 connection 내에서 통신하며 병렬 HTTP request를 처리할 수 있는 것이다.

HTTP header compression

위에서 언급했듯, HTTP/1.x의 header size는 꽤 크다. 이를 위해 HTTP/2의 2가지 기능을 소개할 것이다.

첫 번째는 Huffman coding으로, 무손실 데이터 압축에 주로 사용되는 알고리즘이다. 이를 통해 header size를 약간 줄일 수 있다.

두 번째는 Header tables이다. Header table을 두고, header를 문자열이 아닌 index 형태로 보내는 것이다. header name은 static table index로 대체하고, header field는 dynamic table index로 대체하는 것이다. 이부분에 대한 설명은 아래 reference [1]에 자세하게 나와있으니, 해당 문서를 참고하기 바란다.

HTTP/1.x에서는 KB 단위의 HTTP header들을 매 요청마다 동일한 값으로 반복해서 보냈으나 HTTP/2에서는 위 기능을 통해 최소한의 데이터로 전송한다.

HTTP/2 그 외 기능들

이외에 HTTP/2에 새롭게 추가된 기능들도 있다. 예를 들면 Server push, stream priority 등이 있다.

Server push는 이름그대로 Client의 요청 없이 Server에서 리소스들을 알아서 보내주는 것이다. Client에서 요청을 보내지 않아도 된다는 이점이 있다.

여담: How to enable HTTP/2 on Spring server?

문서를 정리하면서 의문이 생겼다. “그래서, 내가 개발하고 있는 서버에서는 어떻게 HTTP/2를 사용하지?”

그래서 Spring 레퍼런스에서 관련 내용들을 찾아보았다. 보통 embedded server로 Tomcat 또는 Netty를 사용하니, 아래 문서를 참고하면 도움이 될 것이다. 다행히 설정 방법은 간단해보였다.

Conclusion

HTTP/1 vs HTTP/1.1 차이점(persistent connection)을 먼저 살펴보았고, HTTP/1.x vs HTTP/2 차이점도 살펴보았다. 여러 HTTP request들을 위해 TCP Connection을 최대한 재사용하는 것이 핵심이라고 할 수 있다. 또한 불필요한 요청 수를 줄이고, Header compression을 통해 요청의 크기를 최소화하였다.

HTTP/2에 대한 더 자세한 내용 (e.g. Frame-level의 동작방식 및 Server push 기능 등) 또는 gRPC에 Server/Client/Bidirectional streaming 기능들이 HTTP/2에서 어떻게 동작하는지 궁금하다. 다음에 이에 대해서도 포스팅할 기회가 생기면 다뤄볼 예정이다.

틀린 내용 지적은 언제나 환영이니, 댓글 남겨주시기 바랍니다!

References

[1]: HTTP/2

[2]: RTT

[3]: HTTP persistent connection (wikipedia)

[4]: RFC HTTP/2: streams and multiplexing

[5]: HTTP/2 (이미지 참고)

[6]: HTTP pipelining

[7]: Head-of-line blocking (wikipedia)

--

--