풀스택서비스네트워킹(8), HTTP/2

2022. 12. 11. 19:09강의 내용 정리/풀스택서비스네트워킹

728x90
반응형

HTTP/2


1. HTTP/1.1의 문제점

1) 웹의 역사

(1) 웹의 역사

웹은 팀버너스리가 문자, 사진, 동영상, 음성 등의 연구결과를 공유하기 위해 만들었다. 이때 하이퍼텍스트의 개념을 구현해서 클릭을 하면 넘어가는 식으로 진행되었다. 이때 모자이크에서 발전한 넷스케이프가 등장해 브라우저를 만들었다. 이후 웹 브라우저들이 이를 참고해 만들어졌다. 

 

팀 버너스리는 오픈소스인 SOLID를 만들었다. WWW을 만들었을 때 다른 기업의 이익을 위해 만든 것이 아니다! 나의 데이터는 내가 관리하도록 한다!라는 점을 고려해 만든 오픈소스이다.

 

HTTP 2부터는 뒤에 소수점이 없다.

 

(2) 한국에서의 웹

1990년대에 처음 상용화되고, 분당에서 최초로 인터넷이 시작하고, 전국적으로 확산되기 시작했다. 1989년도에 처음 웹이 만들어졌기에 우리나라에 도입된 시기를 고려해보면 매우 빠른 것을 알 수 있다. 이때 확산 속도도 매우 빨랐고, 3세대 이동통신을 최초로 상용화하기도 했다.

 

초기에는 컴퓨터 기반의 게임이 만들어지다가 일본에게 밀렸지만 이후 (1) 리니지와 같은 네트워크 기반의 게임이 등장했고, (2) 음란물의 유통 때문에 매우 빠르게 웹이 확산되었다. 

 

(3) 정상의 문제화

과학 정보를 주고받기 위해 만들었으나 이렇게 많이 사용될지 몰랐다. 3.5세대 이동통신과 스마트폰의 확산에 따라 웹의 사용도 매우 많아졌다. 이후 HTTP는 한계가 있다고 판단되었다.

 

2) Head of Line(HoL) blocking

가장 큰 문제점으로, 머리가 움직이지 못하면 움직이지 못하는 것처럼 request가 연달아 왔을 때 첫번째 request에 대해 response를 못하면 두번째, 세번째 request에 대해 응답도 보낼 수 없다.

 

만약 네이버를 열었을 때 의미없는 아이콘이 못온다면 광고도 보낼 수 없는 상황이 벌어지게 된다. 즉, 비즈니스를 하는 데에 어려움이 생기게 된다.

 

3) Fat message headers

헤더에 너무 많은 정보가 들어가있고, 헤더 정보가 중복되는 경우가 많다. 따라서 HTTP 2.0에서 중복된 정보를 압축하고, 성능을 높이고자 한다.

 

동일한 클라이언트에게 매 요청이 올 때에도 동일한 정보를 주고받는다는 단점이 있다. 심지어 HTTP 리퀘스트의 헤더에 들어가는 정보가 바디에 들어가는 정보보다 더 커지는 경우도 존재하게 된다.

 

또한 HTTP는 stateless이기에 로그인 정보 등과 같은 내용을 쿠키로 저장한다. 이 또한 매 요청마다 헤더에 포함되어서 로그인을 한 적이 있는지 없는지, 아이디, 패스워드 등을 함께 보낸다. 하지만 이는 사람에게 익숙한 문자로 보내기에 보내는데 문제가 있다. 

 

요약하자면 동일한 메세지를 중복적으로 보내고, 사람이 읽기 쉬운 데이터 형태로 보내기에 문제가 발생한다는 점이다. 

 

4) Limited priorities

HTTP 1.1은 우선순위에 따라 메세지를 처리할 수 없다. 하지만 비즈니스를 위해서는 우선순위에 따라 작업을 처리해야했다. 따라서 우선순위를 적용하기 위해 특정한 작업을 하게 되는데 이에 따라 여러 문제가 있었다.

 

이미지 파일과 같은 경우에는 프로그래스 기법에 따라 러프한 이미지를 보낸 뒤, 점차 뚜렷하게 표현하는 방법이다.

 

5) client-driven transmission

클라이언트가 연결요청을 보내고, request를 보내는 동작을 했다. 서버는 이러한 작업이 없으면 아무것도 보낼 수 없었다. 즉, 서버가 클라이언트에게 먼저 정보를 주는 것이 불가능했다.

 

미리 컨텐츠를 내릴 수 없기에 광고를 보내거나 커스터마이즈된 컨텐츠를 제공하는 것이 어려웠다.

 

 


2. HTTP/2 이해

1) SPDY

(1) SPDY란?

구글이 위의 HTTP 1.1의 문제점을 개선해 웹 컨텐츠를 전송하고자 만든 비표준 프로토콜이다.

TCP에서 암호화가 없기에 TLS라는 레이어를 사용해 보안을 사용한다.

 

압축, 다중화, 우선순위 설정을 통한 (1)전송지연과 암호화를 통한 (2)보안을 개선하는 목적으로 만들어졌다.

 

다중화: 여러개를 전달받아도 섞어서 전달한다.

압축: 전송하는 데이터를 줄인다.

우선순위 설정: request를 받은 것 중에 우선순위에 따라 처리한다. 

 

위의 특징을 사용해 전송지연을 줄이고자 했다. 또한 TLS를 사용한 보안을 필수적으로 적용했다.

 

암호화를 하지 않는 경우에는 연결을 지원하지 않았다. 이에 따라 보안은 이슈가 있어서 약화되었다. 그러나 보안은 디폴트로 제공되고, 안쓰면 선택적으로 할 수 있다.

 

(2) SPDY 연혁

내부 프로젝트로 공개

-> 크롬 브라우저에 심어서 오픈소스로 이를 내보냈다.

-> 성능이 좋아서 구글의 모든 서비스에 도입

-> 아파치 서버용으로 제공했고, Nginx에서도 이를 지원

-> IETF 표준화단체에서 표준화를 추진

-> 표준화가 되고, SPDY를 중단한다. 

 

모든 상황에서 HTTP 1.1보다 좋은 것은 아니었지만 대부분의 상황에서는 HTTP 1.1보단 좋았다. 

IETF 단체가 표준화를 추진하긴했지만 브라우저와 서버 사이의 TLS가 문제가 되어서 15년에 표준화를 하면서는 옵셔널로 바뀌었다. 또한 SPDY의 기능이 마이너하게 들어가게 되었다. 

 

cf) TLS를 넣으면서 구글 서버와 크롬 브라우저 사이에서 통신을 진행하는 것은 외부에선 모르게 처리했기에 통신업자들이 문제를 들고 일어났다.

cf) HTTP/2부터는 소수점단위로 표현하지 않는다.

 

2) HTTP/2

(1) HTTP/2 문서

이는 레이턴시를 줄이고(강조점),지연방지를 목적으로 하며, 서버의 부하를 줄인다. 또한 헤더 필드 압축, 동시적으로 멀티플 메세지를 교환하고, 서버에서 클라이언트로 푸쉬하는 동작이 가능하다.

 

구글, 모질라 파운데이션이 이를 했다.

 

우리는 7540 버전을 사용해 얘기할 것이고, 7541버전은 압축 버전을 의미한다.

 

(2) HTTP history

HTTP/3를 개발할 때는 훨씬 더 기간이 짧았다.

 

HTTP2에는 SPDY에서 사용했던 기술이 반영되기도 했다.

 

(3) HTTP/2 점유율

2021년 1월 자료

 

네이버와 넷플릭스도 http/2를 많이 사용한다.

 

최근 네이버는 HTTP/3를 사용하는 식으로 바뀌고 있다.

 

 

3) HTTP/2의 주요 목표

(1) 줄어든 지연 시간

제일 주요한 목표이다. 이를 줄이기 위해서 프로토콜 오버헤드를 최소화한다.

 

(2) 응답 다중화 지원

request의 응답을 순차적으로 처리하지 않는다. 동시에 메세지가 만들어지면 이를 동시에 처리해서 보낸다.  이는 잘 동작한다.

 

(3) HTTP 헤더 필드 압축을 통한 포로토콜 오버헤드 최소화

HTTP 1.1에 있는 헤더 정보는 그대로 있지만 전송할 때 중간 과정에서 압축한다. 이미 보낸 정보인 경우에는 이를 중복적으로 보내지 않고, text based를 0과 1로 축소하여 정보를 줄인다.

 

이는 통신 선로 상에서 오버헤드는 줄이지만 클라이언트 측에서의 압축을 처리하는 오버헤드는 늘어난다. 하지만 PC의 프로세싱 파워가 커졌기 때문에 이를 처리할 수 있는 것이다. 

 

이는 잘 동작한다.  grpc 및에 프로토콜 버퍼가 동작한다는 것을 보면 이 또한 잘 동작하는 것을 알 수 있다.

 

(4) 요청별 우선순위 지정 추가

클라이언트가 서버에게 요청을 5개를 보내지만 우선순위가 높은 3번째 메세지를 먼저 처리해줘~ 혹은 1번째 메세지를 먼저 처리하고 2번째 메세지를 처리해줘~ 등의 동작이 가능하다.

 

하지만 이는 잘 사용되지 않는다. 스케줄러나 자원에 대한 널리지가 있어야 이를 잘 활용할 수 있는데, 이를 잘 활용하기 어려워 처리하기 어렵다.

 

(5) 서버 푸시

사용자의 사용 패턴을 봤을 때 예측되는 다음 화면에 대한 정보를 보낸다. 또한 광고도 내릴 수 있다.

 

이 또한 잘 사용되지 않는다. 이에 따라 브라우저에서 기능을 빼버렸다.

 

(6) 기존 어플리케이션의 수정 없는 지원

기존 HTTP/1.1의 HTTP 메서드, 상태코드, URI 및 헤더 필드 등을 지원한다.

 

 

4) HTTP/1.2가 아닌 HTTP/2인 이유

더 이상 통신 선로 상에서 주고받는 메세지가 text based가 아니고 0과 1로 전달하는 binary framing 계층을 도입했기에 HTTP/2라고 표현한다. 다만 HTTP1.1의 내용을 그대로 사용하고 message가 아니라 frame으로 처리하게 된다. 또한 frame을 각각 여러개로 찢어서 이를 따로따로 전송한다. 덕분에 하나가 통신 선로에서 오래도록 통신되지 않는 것을 방지한다. 덕분에 여러 메세지가 섞여서 전달되고 공평하게 전달된다.

 

찢어진 프레임은 HTTP/2 frame의 기능에 의해 binary framing layer가 비트단위로 처리할 수 있도록 돕는다. 이를 압축하고 TLS가 옵셔널처리되어서 보내게된다.

 

5) HTTP 2 용어

(1) Binary Framing Layer

헤더는 헤더 프레임으로, 바디는 데이터 프레임으로 바꿔서 동작한다. 이때 바디의 크기가 크다면 Data frame을 여러개로 쪼개서 처리한다.

 

기본적으로 HTTP 1.1에서 정의한 메서드를 그대로 사용할 수 있다. 따라서 어플리케이션 레벨에서 이를 이해하고, 네트워크 레벨에서 이를 0과 1로 나눠서 송수신된다.

 

(2) Stream, Message & Frame

스트림은 클라이언트와 서버가 데이터를 주고 받을 때 사용하는 줄을 여러개로 늘려서 처리한다. 하나의 tcp에서 논리적인 파이프(스트림)를 여러개 둬서 동시다발적으로 주고받을 수 있도록 한다. 즉, 리소스를 주고 받을 때 사용하는 줄을 의미한다.

 

 

메세지의 개념은 동일하다. 메세지가 너무 크면 이를 나눠서 처리한다. 메세지는 서비스 레벨에서 의미있는 단위이다.

 

하나의 메세지는 두 개 이상의 프레임으로 쪼개질 수 있고, 각 프레임은 통신의 최소 단위가 된다. 따라서 각 프레임은 서로 독립적으로 전송된다.

 

TCP 연결을 통해서 클라이언트와 서버가 정보를 주고 받는다. 스트림을 나눠서 각 리소스를 가지고 오는 행위가 독립적으로 이뤄질 수 있다.

 

프레임에 스트림 id를 넣어둬서 논리적, 물리적으로 처리한다. 스트림 내에서는 순서가 바뀌어도 상관없이 잘 처리한다. request, response 메세지의 상위 개념으로 stream, 하위 개념으로 frame이 생긴 것으로 볼 수 있다.

 

리쿼스트는 짧기에 하나의 프레임만 전달받아서 사용한다. 리스폰스는 프레임을 쪼개서 전달한다.

 

하나의 TCP 연결을 사용하면서 클라이언트와 서버가 동시다발적으로 stream을 쪼개서 프레임을 전달하는 것을 알 수 있다. 또한 하나의 TCP 연결 내부에서 여러개의 request, response가 페어로 묶이는 것을 알 수 있다. HTTP 1.1은 하나가 안오면 그 뒤에 있는 메세지도 전달받지 못하는 상태이지만 HTTP2는 하나의 메세지를 작은 스트림으로 쪼갰기 때문에 긴 메세지가 오더라도 이를 쪼개기에 조금씩 값을 전달할 수 있다.

 

또한 헤더를 먼저 전달함으로서 인텔리전스한 동작을 할 수 있는 단초를 제공한다.

 

결국 클라이언트와 서버가 단일 tcp 연결을 하더라도 스트림의 개수를 늘리면 멀티플렉스를 할 수 있게 된다.

 

스트림의 수도 제한이 없다. 스트림은 하나의 리퀘스트, 리스폰스의 페어로 볼 수 있지만 논리적인 정보의 구멍으로 볼 수 있다.

 

인터리빙: 큰 정보를 잘게 쪼개서 뿌리는 것

 

(3) HTTP/1.1 문제

HTTP/1.x에서는 어떤 메세지를 하나 보냈을 때 이전 메세지가 도착하지 않았더라면 뒤에 있는 메세지를 제대로 받지 못했다. 따라서 이를 해결하기 위해 메세지마다 TCP 연결을 진행했다. 하지만 이는 한계가 있었고, address sharding이라 해서 물리적인 서버는 한대이지만 도메인 이름을 여러 개로 해서 논리적으로는 여러 컴퓨터로 전달받도록 했다. 결국 TCP 연결을 많이 해서 동시다발적인 전송을 가능하게하고자 했다. 

 

HOL(Head of Line, 앞에 있는 것이 응답이 오지 않아 뒤에 있는 것도 응답을 받을 수 없다.)를 조금 완화하긴 했다. 하지만 TCP 연결과 같은 CPU와 메모리를 비효율적으로 매우 많이 사용한다는 점을 초래한다는 단점이 있었다. 즉, 서버 측의 부하가 매우 늘어나게 됐다. 

 

(4) 요청 및 응답 다중화

클라이언트와 서버가 HTTP 메세지를 주고 받는다. 다만 순서가 섞여서 전달된다. 하나의 큰 데이터는 두 개 이상의 프레임으로 쪼개지고 동일한 스트림도 헤더와 데이터가 쪼개져서 전달되니 인터리빙 혹은 병렬처리를 통한 송수신이 가능하게 됐다. 즉, 서버는 request를 받은 순서대로 response하지 않아도 된다.

 

스트림을 5개 만들어서 request, response 페어가 독립적으로 보내진다. 복수의 메세지가 섞여서 클라이언트에서 서버, 서버에서 클라이언트로 보내지기에 다중화가 가능하게 되었다.

 

(5) HTTP/2 바이너리 프레이밍 계층의 다중화 효과

TCP 연결은 하나만 하여 메모리와 CPU 사용을 줄인다. 이를 통해 이미지 스프라이트(이미지를 어렴풋이 보내고 점차 뚜렷하게 처리한다.) 등의 기술들도 적용이 가능하게 된다. 

 

또한 이를 통해 불필요한 지연을 제거하고 네트워크 용량의 활용도 개선하고, 페이지 로드 시간(반응 시간)을 줄이게 된다. 결론적으로 HOL을 해결하고, 병렬처리 등을 하고 서버쪽의 부하를 줄여서 배포 비용을 절감하게 된다.

 

 

cf) HTTP2는 TLS를 포함하는 개념을 의미하고, HTTPS는 HTTP/1.1과 TLS를 연결하는 것을 의미한다. 브라우저는 도메인 네임을 쳤을 때 서버에서 HTTP2를 지원한다면 해당 스킴을 사용하고, 이를 지원하지 않으면 HTTP/1.1 + TLS(HTTPS)로 연결하고, 만약 이 또한 연결안되면 HTTP 1.1로 연결한다.

 

 

4) 스트림 우선 순위 지정

전달한 홈페이지에서 어느 부분을 먼저 보낼지 정할 수 있다. 클라이언트가 가장 궁금해할만한 부분을 먼저 공개한다. 여러 데이터를 주고받을 때 우선순위에 따라 이를 전달할 수 있게 된다. 이를 위해 두 가지 스킴을 사용할 수 있다. 가중치와 종속성이 이에 대한 내용이다. super와 sub에 대한 내용을 파악하고 super를 먼저 처리한다. 즉, 상하 관계에 대해 파악하여 이를 처리할 수 있고, 가중치에 따라 중요도를 판단하여 전달할 수 있다.

 

클라이언트가 우선순위 지정 트리를 구성하고 통신할 수 있다. 이후 클라이언트는 우선순위 지정 트리를 서버에게 전달한다. 서버는 클라이언트가 전달한 우선순위에 따라 스케줄링하고, CPU, 메모리 및 기타 리소스의 할당을 제어한다. 즉, 클라이언트 쪽으로 부하를 전달한 것이 된다. 이론적으로는 좋지만 실질적으로는 많이 사용하지 않을 수 있다. 

 

과거에는 이를 처리하기 매우 어려웠다.

 

(1) 스트림 우선 순위 동작 원리 

스트림 종속성

우선순위가 높은 것은 상위 요소로 올라간다. 상위 요소가 없으면 루트 스트림에 종속시킨다. 상위 요소에 해당하는 것이 위에 올라가고 리소스도 먼저 할당되어서 먼저 처리된다. 

 

상하 관계인 경우에는 waiting factor는 큰 의미가 없어진다.

 

 

스트림 자원 할당(가중치)

상하관계가 아닌 동일한 관계인 경우에는 가중치에 따라 리소스를 더 할당할 수 있다. 이때 모든 가중치를 더해서 각 비율에 따라 이를 전달할 수 있다.

 

위의 예시에서는 A를 3번, B는 1번 리소스를 전달하는 식으로 처리된다. 이를 인터리빙이라 한다.

 

(2) 스트림 우선 순위 지원 효과

클라이언트가 우선순위 지정 트리를 구성하고 있고, 필요할 때에 이를 서버에 적용해서 처리할 수 있다. 즉, 유연성있게 이를 처리할 수 있어서 유리하다. 성능 또한 개선할 수 있다.

 

 

(3) 스트림 우선순위 지원 주의사항

종속성, 가중치는 표준에 있지만 서버는 이를 항상 준수할 필요는 없다. 즉, 클라이언트는 이를 전달하긴 하지만 서버에게 강요할 수 없다. 도구는 있지만 도구를 사용할지에 대한 얘기는 서버가 결정한다.

 

 

5) One {TCP} connection per origin

(1) HTTP/2의 단일 TCP 연결을 통한 Client-server 통신

클라이언트와 서버와는 하나의 TCP 연결만 유지한다. HTTP 1.1에서는 TCP의 성능을 끌어올리기 위해서 여러 TCP연결을 했지만 이는 서버 부하가 매우 컸다. HTTP 2는 TCP 연결을 하나만 한다. 

 

HTTP 2 연결은 영구적이다. 이때 클라이언트가 서버의 웹페이지를 처음 본 시점부터 그 이후에는 웹 페이지를 다시 열더라도 새롭게 열지 않고, 처음 열었던 TCP 세션을 그대로 사용한다. 어플리케이션이 끝날 때까지는 그대로 계속 유지하게 된다. 서버에서의 부하가 많이 줄어들게 된다. 제일 중요한 것은 TCP 세션 수가 매우 줄어들었다는 것이다. 두번째로는 보안에서의 성능 개선 유리해진다. 보안 성능에서 가장 부하가 많이 드는 시점은 연결 설정과 해제할 때 암호화된 키를 생성하고 번역할 때이다. 즉, TLS 핸드셰이크에서 매우 많은 부하가 발생하는데, 이 또한 줄어든다는 장점이 있다.

 

(2) HTTP/2의 단일 TCP 연결 사용 시 성능 개선 사항

HTTP 1.1 전송은 수명이 짧고 request에 비해 response가 매우 많은데 반면에 TCP는 연결설정을 한 뒤 처음에는 메세지를 세그먼트를 하나만 전달한다. (슬로우 스타트) 따라서 HTTP와는 안맞게 된다. 이에 따라 HTTP/2에서는 동일한 연결을 재사용해서 TCP 연결을 더욱 효율적으로 사용할 수 있다.

 

HTTP에서의 request들 간에는 독립적이게 된다. 하지만 TCP 연결 하나에서 주고받기에 앞에 있는 메세지가 뒤에 있는 메세지에게 영향을 미치게 된다. 즉, 이전에 보낸 것을 보고 전후관계를 파악해서 이전에 보낸 것을 압축해서 처리한다. 이에 따라 전반적인 프로토콜 오버헤드를 줄일 수 있게 된다.

 

cf) HTTP 1.1에서는 압축이 안되지만 HTTP 2에서는 압축이 되는 이유를 설명하시오: 이에 대한 답변으로는 단일 TCP 연결을 통해 압축이 가능하게된 배경을 설명하면 된다.

 

또한 적은 연결을 사용하기에 서버의 메모리와 처리량이 줄어들게 된다. 클라이언트도 TCP를 여러개 연결하는 것은 부담이 될 수 있다. 프록시 서버들도 부하가 발생하기도 한다. 

 

운영 비용이 줄어들게 되고 통신 링크 상에서 동시다발적으로 주고받기에 네트워크 활용도와 용량이 개선된다. 눈에 띄는 개선 사항은 지연 시간이 줄어드는 것이다. 여러 요소가 동시에 보여진다는 장점이 있다.

 

 

6) 흐름 제어

HTTP2는 흐름 제어를 한다. TCP도 flow control이 있다. 그 위에서 동시다발적으로 여러 스트림을 주고받을 수 있도록 하는데 이에 대한 흐름 제어를 한다. 

 

클라이언트가 서버에 붙어서 동영상 스트리밍을 보는데, 이때 우선순위에 따라 영상 스트림을 가장 먼저 보내야한다. 이때 만약 유튜브를 띄어 놓고 동영상을 보게 되면 영상을 멈추더라도 버퍼링을 계속 보낸다. 하지만 만약 다른 영상을 틀게 되면 서버와 클라이언트는 사용하지 않을 데이터를 통신하게 된 것이다. HTTP 2에서의 흐름 제어는 이러한 경우에는 트래픽을 막아버리는 처리를 할 수 있다. 이는 어플리케이션의 입장을 대변하게 된다. 하지만 아직까지는 HTTP의 이런 특징을 사용하는 경우는 그렇게 많지는 않다. 

 

특정 스트림의 리소스를 조절할 수도 있다. 스트림 레벨에서 이를 처리하는 것이다. 프록시 서버가 중간에 있을 때 다운스트림(서버에서 내려오는 경우), 업스트림을 할 때 양방향의 속도를 맞춰주는 역할도 한다.

 

(1) HTTP/2 흐름 제어 특징

TCP는 흐름 제어를 한다. 슬로우 스타터와 같이 스스로 흐름제어를 알아서 한다. 하지만 HTTP2는 흐름제어를 위한 특정 알고리즘을 지정하지 않고, 이를 개발하는 사람의 역량에 맡긴다. 즉, 기능은 존재해서 빌딩 블록을 제공하지만 이에 대한 구체적인 구현과 운용은 클라이언트와 서버를 짜는 사람이 진행하게 된다.

 

네트워크의 트래픽은 패턴이 많이 없다. 따라서 AI를 투입하는 것은 조금 어렵다. 따라서 통찰력이 더욱 중요하게 된다. 오픈 소스로 할 수 있는 것은 많아졌지만 이를 통찰력있게 필요한 부분에 적용해 사용하는 것이 중요해진다.

 

웹 어플리케이션의 실제 성능과 측정된 성능을 모두 개선할 수 있는 새로운 전달 기능도 구현하도록 지원한다. 구체적인 예시로는 스트림 흐름 제어 창을 0으로 줄여서 아예 안가져오도록 설정할 수 있다. 혹은 앞 부분은 살짝 받은 뒤, 볼지 말지를 결정하는 식으로 처리할 수 있다. 이에 따라 서버와 클라이언트의 부하를 줄일 수 있다. 어플리케이션에서 이를 처리하기에 건드릴 수 있는 여지가 많다.

 

(2) HTTP/2 흐름 제어 동작 원리

흐름 제어는 양방향이다. TCP 흐름제어 처럼 주고받는 것에 대해 독립적으로 할 수 있고, 수신단을 타겟으로 해서 송신단과 window 사이즈를 주고 받으며 윈도우 사이즈가 크면 더 많이 주고받고, 작으면 덜 주고받을 수 있다. 이를 스트림 단에서 처리한다. 또한 스트림을 묶어서도 윈도우를 조절할 수 있다. 

 

흐름 제어는 크레딧 기반이다. 크레딧은 바이트 단위로 이뤄진 버퍼 크기인 윈도우를 가지고 처리한다. 윈도우가 있고, 이를 주기적으로 window_update 프레임을 수신단에게 보내서 윈도우 사이즈를 알려주고, 송신단은 이를 보고 얼마나 보낼지 결정할 수 있다.

 

흐름제어는 비활성화될 수 없다. 따라서 보내지 마~ 라고 얘기하더라도 실질적으로 흐름 제어를 끊은 것이 아니라 계속 흐름제어를 하고 있는 상태가 된다. 수신단의 사이즈를 송신단에게 알려주는 절차가 필요한데 이는 SETTING을 주고받으면서 처리한다. 여기서 흐름제어와 관련된 창 크기를 서로 주고 받는다. 기본적으로 어플리케이션 단에서 흐름제어를 하기에 TCP의 흐름제어와는 다르게 흐름제어를 따로 가지고 있다.

 

흐름제어 창의 사이즈는 TCP와 마찬가지로 64kb로 디폴트 값이 설정되어있다. TCP는 오래된 프로토콜이기에 제한된 비트 수를 가지고 하기에 수십키로 바이트 이상의 수신단 버퍼를 만드는 것이 불가능했지만 HTTP는 WINDOW_UPDATE를 하며 최대 창의 크기를 2의 31승 -1(2GB) 바이트만큼 설정하고 유지할 수 있다. 기본적으로 어플리케이션 단에서 스트림을 멈췄다가 보내는 기능적인 유연성도 있지만 수신단의 버퍼를 늘릴 수 있기에 속도를 올리고 기능을 개선하게 된다.

 

TCP에서의 흐름제어는 end-to-end로 두 개 사이의 일이었다면 HTTP/2는 홉 방식이다. AtoB, BtoC 등, 프록시 서버가 있다면 서버와 프록시, 프록시와 클라이언트 등 여러 관계 각각에 대해 흐름제어가 독립적으로 가능하게 된다. HTTP2라면 클라이언트와 서버 사이에 프록시가 존재하는데 이는 매우 중요해진다. 클라이언트와 프록시, 프록시와 서버는 서로 암호화된 연결을 뚫는다. 프록시는 캐시나 보안 등의 기능을 한다. 

 

요약

TCP의 흐름제어와 유사하게 HTTP/2의 흐름제어도 존재하지만 TCP의 흐름제어는 사람이 조절할 수 없다. 하지만 HTTP 흐름제어는 프로그래밍을 하는 개발자에 의해 다양하게 알고리즘을 짤 수 있다.

 

 

7) 서버 푸시

교내 계정을 구글 계정으로 연결하면 학교의 정보를 순식간에 한번에 전달 받을 수 있다. 이는 푸시 이메일이라 해서 서버에 도착하는 순간에 클라이언트에게 한번에 전달한다. 하지만 그 외의 경우에는 시간 단위로 클라이언트에서 서버에 있는 정보를 가져온다. 결국 서버에서 클라이언트에게 정보를 내려주고 싶으면 서버에서 바로 정보를 내려주는 것을 의미한다. 가장 큰 예시는 광고가 될 수 있다. 순기능적으로는 클라이언트가 필요한 것을 서버가 미리 파악해서 이를 요청하지 않아도 미리 파일이나 코드 등을 내려주는 것과 같은 처리가 가능하다. 

 

stream 2와 4는 promise의 형태로 전달된다. 즉, 클라이언트가 서버에게 요청하진 않지만 서버가 보내주기로 약속해서 이를 전달하는 것을 의미한다. 따라서 클라이언트가 데이터를 요청했을 때 전달하는 것이 아니기에 지연을 줄일 수 있게 된다.

 

stream 4는 promise와 frame으로 나눠지는데, 앞에 있는 promise는 뒤에 있을 frame을 설명하는 것이 된다. 따라서 이는 헤더와 같이 동작한다.

 

(1) 서버 푸시 기능의 필요성

HTTP 베이스를 통해 웹 페이지를 구성하는 경우에는 매우 많은 리소스를 사용하게 된다. 따라서 서버가 이를 미리 전달하게 되면 그만큼의 지연 시간을 줄일 수 있게 된다. 

 

물론 사용자가 이를 사용하지 않게 되면 내려보낸 리소스는 버려지게 된다. 이러한 경우에는 네트워크 속도가 더 느려지지만 일반적으로는 response time을 줄일 수 있게 된다. 이 부분이 매우 중요한 이유가 된다.

 

CSS나 자바스크립트를 웹 페이지에서 사용하기에 매우 꼬이게 될 수 있다. 필요한 파일을 하나의 HTML이나 자바스크립트 코드 등으로 만들어서 표현한다. 이에 따라 이를 유지보수하는 작업은 매우 오래 걸리게 된다. 만약 서버 푸시 기능을 잘 사용한다면 기능을 분리할 수 있기 때문에 관리나 유지보수 등이 편하게 되고, 직관적으로 소프트웨어를 개발할 수 있게 된다. 

 

(2) 서버 푸시 동작

서버가 컨텐츠를 내려보낼지 말지 먼저 판단한다. 미리 내릴 것들을 stream으로 규정하고, PUSH_PROMISE하여 이를 클라이언트에게 미리 알려준다. 클라이언트는 이미 가지고 있는 데이터인 경우에는 굳이 이 데이터를 받지 않아도 되기에 클라이언트가 이를 받을지 말지 판단하여 처리할 수 있게 된다. PUSH_PROMISE는 HTTP 헤더 정보를 가지고 있다.

 

클라이언트는 이후 결정을 할 수 있다. 첫번째로는 거부할 수 있다. 이 경우에는 RST_STREAM을 사용한다. 클라이언트가 이미 그 데이터를 가지고 있어서 더 이상 받을 생각이 없으면 거절할 수 있다. 두번째로는 받을수도 있다. 각 push할 것들이 독립적인 stream으로 내려올 수 있다. 이를 어떻게 받을지 조절할 수 있다. 서버에서 클라이언트 쪽으로 동시에 몇 개의 push stream을 열지(동시에 몇개를 받을지) 클라이언트가 조절할 수 있다. 또한 flow control도 제어할 수 있다. 

 

그리고 마지막으로는 클라이언트가 정한 것에 의거해 push가 된다. 이때 multiplex나 우선순위화되어서 보내질 수 있다. 여러 스트림이 있지만 하나의 TCP 연결에서 이를 사용하고, 보안은 TCP에서 처리한다. 따라서 각 스트림들은 하나의 TCP 보안 규칙을 따르고 있다. 따라서 서버 푸시를 하더라도 암호화관련되어서 성능 저하가 발생하지는 않다. (연결될 때 한번만 사용하면 되기 때문이다.)

 

 

(3) HTTP/2 서버 푸시 현황

하지만 크롬 브라우저에서 푸시를 받는 기능을 제거했다. 2020년에 서베이를 했는데 실제 서버 푸시를 사용하는 비율이 0.05%밖에 안됐기 때문이다. 일반적으로 HTTP 2를 사용하는 이유는 두 가지로 나눠볼 수 있는데 우선 gRPC를 사용하기 위해서 하부에 HTTP/2를 사용하는 경우와 HTTP/1.1에서 코드의 수정 없이 HTTP/2로 옮겨서 latency를 줄이고자 한 경우이다. 즉, HTTP/2를 최적으로 모두 사용하는 경우는 아직 드물다는 것을 알 수 있다. 푸시 기능을 제공하고는 있으나 아직까지는 활성화하기엔 개발자가 모자란 상황이다.

 

 

8) 헤더 압축

본문은 대게 압축이 되어있는 상황이다. 하지만 헤더는 거의 압축이 되어있지 않다. 

500~800 byte 정도 되는 정보가 헤더에 들어간다. 또한 쿠키는 대부분 사용되는데 이 쿠키의 사이즈가 수~수십키로바이트이기에 매우 커지게 된다. 즉, 본체로 보내는 바디 대비 헤더 정보가 매우 커지게 된다. 리퀘스트를 할 때에는 때론 수 kilobytes가 소요된다.

 

 

HPACK compression이라는 압축을 사용한다.

- 이미 보낸 것은다시 안보내는 것

- 앞에서 안간 정보는 비트 기반으로 데이터를 줄인다.

 

HPACK compression 포멧

허프만 코딩 

많이 등장하는 것들은 소량의 코드로, 자주 등장하지 않은 것들은 대량의 코드로 이를 표현하여 VLC를 한다.

 

indexed list

바뀌지 않는 정보들(like 운영체제, 브라우저, 웹서버 소프트웨어의 이름과 버전 등등)은 한번 보낸 뒤에는 다시 보내지 않는다. 

 

하나의 스트림 내의 HTTP request들은 모두 관계가 있다고 본다. 그래서 이전에 보냈던 정보가 바뀌지 않았더라면 이를 다시 보내지 않는다.

 

결국 앞서 전달한 HTTP req와 지금 전달할 HTTP req의 헤더를 봐서 바뀐 부분만 전달한다.

 

 

HTTP/2의 헤더 압축 예제

메세지(Stream1)에 대해 테이블을 만들어서 이를 저장한다. 이후 두번째 requests를 보낼 때 동일한 내용은 보내지 않고, 달라진 정보에 대해서만 보낸다.

 

동일한 스트림이 아니더라도 헤더 압축이 될 수 있다.

 

 

과거에 보냈던 헤더 정보는 테이블로 관리한다. 이 테이블은 논리적으로는 하나이지만 기능적으로는 아래와 같이 두 가지로 나누게 된다. 위쪽은 static table, 아래쪽은 dynamic table이다.

 

static table

static table은 서버와 내용을 주고받지 않았더라도 이미 만들어진 정보이다. 따라서 자주 사용하는 정보들은 미리 table로 만들어놓은 것이 된다.

dynamic table

dynamic table은 클라이언트 서버 간의 통신이 이뤄지면 밑에서부터 채워가는 정보가 된다. 즉, 서비스를 진행하면서 만든 테이블을 의미한다.

 

requests가 오게 되면 헤더 정보에서 table과 중복되는 내용의 인덱스를 보낸다. 만약 table에 있지만 내용에 대해서는 허프만 코딩을 사용해서 해당 값을 인덱스와 함께 내보낸다. 기존 인덱스 정보를 보고 수신단에서는 함께 판단할 수 있다. 만약 table에도 없다면 모두 보낸다.

 

송수신을 할 때에는 데이터가 줄어들지만 프로세싱 파워가 많이 소모될 수 있다. 

 

Header Table 방식

이는 헤더 정보만 압축한다.

 

초기에는 구글이 발표했던 zlib를 사용했었다. 10분의 1 정도로 보내는 정보의 사이즈가 줄어들었다. 2012년에는 보안상의 공격을 받았고, zlib을 버리고, HPACK을 사용했다.

 


3. HTTP/2 개발 환경

1) 웹서버

거의 대부분의 웹서버에 포함을 시켰기에 서버 단에서는 이를 사용하는데 크게 어려움이 없다.

 

2) 브라우저

각 브라우저별로 버전이 있다. 이 버전별로 HTTP 2를 지원하는지 여부를 확인할 수 있다. 지원 가능하면 초록색이다. 대부분의 메이저 브라우저들이 이를 지원한다.

 

3) Contents Delivery Network

CDN을 빌려서 소프트웨어를 올려서 여러 국가들에 대해 각 국가에서 트래픽을 처리하도록 한다. 이에 따라 원래 국가의 트래픽도 줄이고, 속도도 빠르게 할 수 있다. 컨텐츠를 딜리버리하는 회사이다. 애플인 경우에는 미국에 본사만 두고 사용했기에 업데이트를 할 때에도 매우 오래 걸렸지만 지금은 각 국가별로 서버를 구축해서 더 빠르게 처리가 가능하게 되었다.

 

4) Programming Language

표준화 단체는 표준 문서를 만든다. 표준화 단체인 IETF에서 HTTP2의 표준화를 이룬 사람들의 단체가 깃허브에서 개발하고 있는 라이브러리를 알려준다.

 

원래 표준화 단체는 표준문서만 보여줬지만 HTTP2부터는 개발하고 있는 라이브러리도 적극적으로 소개해주고 있다. HTTP3부터는 오픈 소스의 파워가 더욱 쎄지게 된다. 

 

negotiations

보안은 옵셔널이지만 서버들은 실질적으로는 TLS를 디폴트로 가지고 있다. 이 부분에 대해서 암호화를 해야하는지 하지 않아도 되는지 등을 얘기한다.

 

(1) 언어별 HTTP2

표준 라이브러리는 없지만 Hyper가 있다.

임베디드 시스템의 경우에는 가볍게 사용할 수 있기에 C를 사용하는 경우도 많다. C++은 성능이 10배 정도 좋아졌다는 얘기도 있다. 최신 언어들은 엔진과 같이 복잡한 것은 C++로 짜고 링킹하여 다른 언어에서 사용할 수 있게끔 구현한다. FFI: 파이썬으로 짜더라도 다른 언어에 있는 함수를 호출하는 식으로 사용한다. 최근에는 C언어를 연결하는 경우가 매우 많아지고 있는 추세이다. 클래스를 1대1로 연결지어서 사용하기 어렵기 때문이다.

 

(2) Go

Go는 서버용 언어로 스탠다드 라이브러리로 있기에 간단하게 만들 수 있다.

 

 

다트는 휴대폰 어플리케이션 용도로 나왔다. 

 

두 개 이상의 언어로 프로그램을 짜는 것을 의미한다. 전통적으로는 기계어에서 이를 합치는 과정을 했지만 최근에서는 기능별로 쪼개서 Microservice를 사용하기에 서로 다른 언어로 만들고 통신을 통해 사용한다. 

 

스택오버 플로우에 질문이 올라온 것(y축)과 깃허브에 올라온 언어(x축)을 사용해 인기도를 확인할 수 있다.

웹이 대세인 것을 알 수 있다.

 

4. 구현 예제

1) Go 언어 개요

켄 톰프슨: 유닉스 개발자이자 C언어에 영감을 준 B언어의 개발자

 

static typing과 strong하다.

strong

a 타입에 서로 다른 b타입을 넣는 것을 불가능하게 하는 것이 strong이다. 파이썬은 다이나믹하고 strong하지 않고, C/C++/자바 등은 strong하다.

 

static typing

static typing은 변수가 있을 때 그 변수의 타입이 실행과 상관 없이 컴파일할 때 이를 판단한다. 타입이 정해져있지 않으면 컴파일이 실패한다. dynamic typing은 변수의 타입이 실행할 때 이를 판단한다. 타입이 정해져있지 않아도 실행할 수 있으며 중간에 에러가 발생할 수 있다.

 

C언어와는 유사하지만 가비지 콜렉터도 등장한다. 더이상 사용되지 않는 객체를 지우는 역할을 해서 힙에 저장된 변수를 지운다. C언어를 사용해서 도커를 만들었다.

 

 

2) Server certificate file creation

Go 언어는 TLS를 하는게 매우 간단하도록 구현해놨다. HTTP 1과 2를 모두 지원하는 형태로 구현되어있다. 보안을 하기 위해서 서버에 대한 인증서를 처리해야한다. 따라서 외부 기관에게 인증서를 받게 된다. 사이트에 접속할 때에는 제 3의 인증 업체에게 인증받은 것을 가지고 확인한다. 이를 실습하기 위해서는 임시 인증서를 발급 받고 사용한다. 

 

즉, 요약하자면 서버는 제 3의 인증 기관에 인증을 받고, 클라이언트는 웹에 접속했을 때 서버로부터 인증서를 받아 안전한지를 확인받는다. 이후 서버와의 암호화된 통신을 할 수 있게 된다.

 

실습을 진행할 때에는 브라우저에서 이를 접속할 때에는 공인된 핸드쉐이킹이 불가능할 때에는 일반모드로 들어가게 된다.

728x90
반응형