🍏 HTTP/1.1의 문제점
✅ Head of Line (HoL) blocking
- http/1.1에는 시퀀스번호가 없다.
- Client가 Server로 복수 request 전송시, Server는 요청 받은 순서대로 response를 보내야 한다.
- Server가 중간 response 작성에 문제가 있는 경우, 후속 요청들은 모두 Client에 전송되지 못하고 지연(Blocking)된다.
- 웹 브라우저의 사용자를 붙잡게 되는 것. 빨리 보내지 못하면 사용자는 지연을 느끼고 이탈한다. 회사가 서비스를 할 찬스를 놓쳐버리는 것.
<현강 추가>
- http1.1을 쓰는 경우에 브라우저와 서버 사이에 8개 세션을 만들어서 각각 리퀘스트를 받아.
- 물리적 컴퓨터를 여러개 두기는 부담스럽기 떄문에 웹 서버가 이름만 여러개를 가진다. 리소스들을 서로 다른 이름으로 분리.
- HTTP/1.1의 헤더는 많은 메타 정보들을 저장한다
- Human Readable = too much 정보
- 통신이 원활하지 않은 경우에서 인터넷을 하는 것이 어렵다 / 전송 속도의 저하를 체감
- 매 요청 마다 중복된 Header 값을 전송하게 되며 또한 해당 domain에 설정된 cookie 정보도 매 요청 시 마다 헤더에 포함되어 전송한다.
- 전송하려는 값보다 Header 값이 더 큰 경우도 자주 발생
- HTTP Message는 앞뒤 메세지가 독립적이기 때문에 앞에서 많은 메타정보를 넣으면 뒤에도 넣어줘야 함.
- Cookie : 웹 브라우저 위에서 돌아가는 프로그램은 컴퓨터에 있는 디스크에 접근이 불가해요. 웹사이트에서 지난번에 접속한 사이트에 다시 접속을 하면 내가 로그인을 안 한 상태에서도 상대방을 유추할 수 있어요. 근데 이마저도 매 요청마다 따라가야 된다는 겁니다. HTTP 메시지는 앞과 뒤에 인디펜던트 아니 뒤에 있는 메시지도 모든 정보를 다 가져가야 되는 거죠.
✅ Limited priorities
- http/1.1은 화면은 채우고 있는 모든 정보는 동일한 우선순위
- 상업목적의 인터넷에서 사용자 요구에 따라 우선순위를 높게 줄 수 없기 때문에 고객이 떠날 수 있다.
- Progress 방식으로 이미지 보여주는 꼼수
- 처음 흐릿한 이미지를 일단 띄워서 고객이 떠나지 않도록 하고 그 위를 덮어씌워 또렷한 이미지 로드
✅ Client-driven Transmission
- Client가 Server로 정보 요청을 해야만 통신이 가능한 구조
- Client가 요구하지 않은 것을 Server가 보내는 것은 불가능하다.
🍏 HTTP/2
✅ Google SPDY (Proprietary Solution)
구글 웹서비스 개발자가 http1.1의 문제를 스스로 개선하고, 오픈소스를 만들어서 레이어를 끼워넣었다.
- 설계 철학 : 웹 페이지 전송 지연을 줄이고, 보안을 개선하자!
- 압축, 다중화, 우선 순위 설정
= 전송 지연 감소 = 사용자의 만족도 up = 고객 확보
- 보안 : SSL/TLS를 무조건 써라! -> 이슈가 좀 있다
- 구글이 크롬 브라우저의 오픈소스 엔진에 탑재
= 이미 고객들에게 SPDY를 쥐어준 것 / 구글이 웬일..?
- 2021기준 전 세계 웹사이트의 45.9%가 HTTP/2를 사용
1.1 문제점
- 성능
시퀀스번호가 없는 이유로 순서를 바꿀수 없음
- 보안
SSL/TLS 레이어가 있었기 때문에 새롭게 만들 필요는 없었다. 얘네를 이용해서 암호화된 통신을 하자!
✅ HTTP/2 주요 목표
- 지연 시간 감소 ⭐⭐
- 응답 다중화 지원
- HTTP 헤더 필드 압축을 통한 프로토콜 오버헤드 최소화
- 요청 별 우선 순위 지정 추가
- 서버 푸시
- 클라이언트에 대한 정보를 알고 있는 서버가 미리 정보를 내리는 것 (추후 크롬에서 삭제됨)
- 기존 어플리케이션의 수정없는 지원
- HTTP 메소드, 상태 코드, URI 및 헤더 필드 등 지원
✅ HTTP/1.2가 아닌 HTTP/2인 이유
- HTTP/1.1 : human readable / text기반 전송
- HTTP/2 : binary framing layer 도입
0과 1의 bit 레벨 단위로 변신했다는 것 / frame 사용 가능
✅ Binary Framing Layer
- HTTP/2는 HTTP/1.1의 메소드(GET/PUT/DELETE/HOST)를 사용해야 하기 때문에 사용되는 데이터들을 HTTP/2에서 정의한 binary형태로 변환할 수 있도록 했다.
- Message가 frame들로 나누어져 송수신이 되는 것.
- header에 해당하는 것은 HEADERS frame으로 / 데이터는 DATA frame으로 쪼개서 들어간다
✅ Stream & Message & Frame
Basic Term
- Stream : 메세지를 그룹화 = 하나 이상의 메세지가 전달될 수 있다
- 바이트의 양방향 흐름
- 양방향 메세지 전달에 사용되는 고유 식별자와 우선순위정보(선택)가 있다.
- Message : 하나의 논리적인 HTTP 메세지(ex-req/res)
- Frame : HTTP/2에서 통신의 최소 단위 / 각 최소 단위에는 하나의 프레임 헤더가 포함된다.
- 프레임 헤더에 삽입된 Stream 식별자를 통해 속하는 Stream을 식별하고 다시 프레임을 조립할 수 있다.
단일 TCP 연결
- 모든 통신은 단일 TCP 연결
- 단일 TCP 내에 Stream 1부터 N까지 동시다발적으로 송수신 가능
- 전달될 수 있는 양방향 Stream의 수는 제한이 없다.
- 주고받는 메세지들을 Frame단위로 쪼갰다.
- HTTP/1.1은 message가 최소단위였기 때문에 한 메세지가 처리 중이면 나머지가 전부 기다려야 했다.
- Frame은 단위가 작기 때문에 전송 사이사이에 정보를 보낼 수 있기 때문에, 사용자에게 조금씩 가는 것을 보여주면 떠나지 않도록할 수 있다.
- Header랑 data를 분리하는 이유
- Header를 별도로 먼저 전달하면 더 똑똑하게 기능할 수 있다. 브라우저나 server가 미리 사이즈와 파일에 대한 정보를 받아서 필요한 작업들을 알 수 있다.
🍏 HTTP/2 - 요청 및 응답 다중화
HTTP/1.1의 문제
- Client의 병렬 요청을 수행하려면 여러 TCP 연결이 필요하다.
- TCP 연결을 한다는 것 : 버퍼를 사용하고, 상태정보를 유지하고, 재전송을 위해 정보를 누적 & 업데이트, Flow Control을 위해 window size 조정 -> 하는일 많다 / CPU & Memory 많이 사용
HTTP/2의 다중화
- Binary Framing Layer 도입
- 동시다발적으로 req/res를 주고 받는다.
- 0 과 1로 변환하여 data 양을 줄인다.
- Frame으로 쪼개져서 병렬 처리
- 왼쪽 : HTTP/1.1 / 오른쪽 : HTTP/2
HTTP/2의 다중화의 효과
= 애플리케이션이 더 빠르고 단순해지며 배포 비용이 절감된다.
- 여러 요청/응답에도 병렬로 인터리빙(쪼개져서 발사) 가능
- 단일 TCP 연결
- 단일 서버를 사용하는 사용자 수 증가
- CPU/Memory 부하 줄인다
- 페이지 로드 시간 감소
- 불필요한 지연 시간 제거
- 가용 네트워크 용량의 활용도를 개선
- HTTP/1.1의 HOL 차단문제 해결
🍏 HTTP/2 - 스트림 우선 순위 지정
가중치 / 종속성
- HTTP/1.1인경우 모든 req/res는 동등한 입장이다. 앞의 정보가 처리되지 않으면 뒤의 정보도 차단 당했다. 즉, 앞이 우선순위가 높았다(FIFO)
- HTTP/2에서는 우선순위가 높은 애들을 골라낼 수 있는 기능을 지원한다.
- resource에 대해 req/res를 Stream으로 처리하니까 Stream 단위로 처리
- req를 받았을 때 서버 입장에서 고객이 가장 궁금할 부분을 먼저 전달해서 고객이 떠나지 않도록 한다.
- 클라이언트가 '우선순위 지정 트리'를 구성하고 서버에게 전달. 서버는 이 정보를 기반으로 Scheduling. CPU, Memory 및 기타 리소스 할당을 제어하여 Stream 처리의 우선순위를 지정한다.
- 왜 클라이언트가?
서버가 하면 힘드니까. 이 기술을 만든 것은 구글
- 서버는 우선순위가 높은 애들을 효율적으로 빠르게 보낼 수 있다. 무선에서 특히 유의미하다.
✅ 스트림 우선 순위 지정 - 동작
스트림 종속성
- 상하 관계 -> 트리
- 자신 보다 높은 우선 순위인 스트림의 고유 식별자를 상위 요소로 참조
- 식별자가 생략되면 스트림이 루트 스트림에 종속된다.
- 상위 요소 스트림에 종속성보다 리소스가 먼저 할당되어야 함을 나타낸다.
- 웹 사이트 화면을 구성하더라도 과거에는 화면 그냥 채우기, 지금은 메뉴 구성을 마치고 우선 순위가 높은 이미지를 먼저 채우는 등으로 이용
스트림 자원(리소스) 할당
- 동일한 상위 요소를 공유하는 스트림은 가중치에 따라 리소스가 할당된다.
- Ex) A 가충치가 12, B가중치가 4
= A 3개 보내고 B 1개 보내고
Example
-
1번 : 대등관계 둘에 대해서 가중치를 정하는 것 (A,B)
-
2번 : D/C 상관관계가 있어서 선후로 정의하는 것. D가 먼저 처리되어야 C가 실행
-
3번 : D 끝나면 C / C 끝나면 A,B를 가중치에 맞춰 보냄. 큰걸 쪼개서 12개와 4개로 적절히 섞으니까 multiplexing 효과도 높아진다.
✅ 스트림 우선 순위 지정 효과
- Client가 기본 설정을 언제든 업데이트 가능, 브라우저 성능 더욱 최적화
- 사용자 상호작용과 기타 신호에 응답하여 종속성을 변경하고 가중치를 재할당할 수 있다.
- 주의 사항
- 종속성/가중치는 특정 처리나 전송 순서를 보장하는 것은 XX
- 서버는 Client의 요구를 받았다고 반드시 그렇게 하지는 않는다. 서버는 참고자료로 사용할 뿐. 무조건 client의 요구대로 수행하는 것은 XX
- 우선순위 높은 리소스가 차단됐다고 낮은 리소스 서버진행이 차단 되는 것을 원하는 것이 아니기 때문에 바람직한 동작
- 서버쪽으로 기술을 발전시킨 구글은 서버가 효과적으로 쓰이는 것은 동의하지만 부담을 느끼기는 원하지 않았기 때문
🍏 HTTP/2 - 단일 TCP 연결
- HTTP/1 = Req 개수만큼 다 받고나면 TCP 연결을 끊어버렸다. URL 엔터 누를 때 열고, X 누를때 끄고.
- TCP를 여러개 열어서 사용할 경우 성능 부하가 심했다.
- 독립적인 Req/Res
- HTTP/2 = 단일 TCP 연결
- Client가 처음으로 웹페이지를 볼 때 TCP 연결하고, 웹페이지를 다시 열 때 처음 TCP Session을 유지. App이라면 TCP 연결을 하나 뚫고, app이 끝날때 까지 연결을 계속 유지
- TCP = 오랫동안 무엇을 주고받는 것에 적합
성능 개선 사항
- 보안 관련 부하 감소
- 연결 설정/해지하는 시점에서 암호화 관련 키의 생성 & 번역 과정에서 부하가 크다.
- TCP 연결이 한 개 = 각 TCP 연결마다 TLS에 대한 핸드셰이크(연결설정과정)도 줄어든다.
- TCP연결이 하나니까 TLS 넣어볼만 하다!
- 결과
- 전체 운영 비용이 절감
- 네트워크 활용도와 용량이 개선
- 네트워크 지연 시간 감소 = 고객 이탈 감소
- 네트워크 처리량이 개선
🍏 HTTP/2 - 흐름 제어
✅ 일반적 흐름 제어 개념
송신단이 불필요한 데이터나 처리가 불가능한 데이터를 보낼 때, 수신단에 부담을 주는 것을 막는다.
특정 Stream에 일정 크기의 리소스만 할당하자!
전송 중단 기능
- 클라이언트가 높은 우선순위로 대용량 동영상 스트림을 요청했지만 사용자가 이 동영상을 일시중지한 경우
- 불필요한 데이터 가져오기와 버퍼링을 막기 위해 서버로부터의 동영상 전달을 일시중지하거나 차단하려고 하는 경우
- 내 수신 버퍼의 가용 버퍼는 0이야~
- TCP의 흐름제어에서 하지 못하는 것. 수신자가 파일을 막 보낼 때 한도 내에서는 계속해서 받을 수 밖에 없다.
- http2에서의 흐름 제어는 application의 입장을 대변한다고 볼 수 있다.
업/다운스트림의 비율 맞추기
- 프록시 서버의 다운스트림 연결은 빠르고 업스트림 연결은 느린경우, 프록시 서버가 업스트림 속도에 맞게 다운스트림의 데이터 전달 속도를 조절하여 리소스 사용량을 제어하는 경우
- 원래 Flow Control의 취지 였지만 TCP에서는 하지 못한 것 -
✅ HTTP/2 흐름 제어 - 특징
흐름 제어를 구현하기 위한 특정 알고리즘 지원 X
- TCP는 흐름 제어 알고리즘이 주어졌다. 그 알고리즘이 일부 세팅에 손을 댈 수는 있지만 동작이 시작하면 스스로 흐름제어를 했다. ex) window size 관련 동작 알아서 처리.
- HTTP/2에서는 정해진 알고리즘이 없다.
- 빌딩 블록을 제공하고 구현은 Client/Server에게 맡긴다. 직접 전략을 구현하여 리소스 사용과 할당을 제어한다.
✅ HTTP/2 흐름 제어 - 동작 원리
흐름제어 : 양방향
- 수신 버퍼의 크기를 정하는 것
- 스트림들을 모두 모아서 컨트롤할수도 있고 각 스트림마다 독립적으로도 가능하고
흐름 제어는 비활성화될 수 없다
- HTTP/2 연결을 할 때 client-server는 수신버퍼 사이즈를 교환해야하기 때문에 별도의 메세지로 정의했다(SETTINGS Frame)
HTTP/2의 흐름 제어 : Hop-by-Hop
다양한 시나리오로 흐름 제어
HTTP/2의 흐름 제어는 http2를 프로그래밍하는 개발자에 의해서 다양한 시나리오에 의해 동작시킬 수 있다.
HTTP/2 vs TCP
- application level에서 보내!멈춰!를 할 수 있는 기능적인 Flexibility
- window size의 크기
- TCP는 애초 오래된 프로토콜이고 커널 안에 있고 제한된 비트 안에서 존재. 엄청 큰 버퍼를 만드는 것이 불가능 했다.
- HTTP/2 수신단의 버퍼 사이즈는 2기가 까지 가능. 어마어마한 사이즈. application level에서 상대방에게 쏠 수 있는 양이 늘어남. 속도와 기능 개선.
- Hop-to-Hop
🍏 HTTP/2 - 서버 푸시
-
서버가 클라이언트(브라우저)의 요청이 없어도 추가적인 리소스를 푸쉬할 수 있다. = 필요한 서비스를 내린다
-
HTTP/1.1에서는 Request가 와야만 Response를 보낼 수 있었다.
-
promise (new!!)
너가 나에게 요청하지는 않았지만 서버 푸시로 이 파일들을 내리려고 해. 니가 언젠가 필요할거야 너의 허가 없어도 내릴게.
✅ 서버 푸시 - 필요성
- 서버가 그런 것들을 미리 헤드 타임이니까 미리 다음 단계에서 필요할 만한 콘텐츠들을 미리 내린다면 결국은 사용자 입장에서의 반응 속도는 줄어드는 거죠.
- 미리 데이터를 내림으로써 사용자가 사용하지 않을 경우 네트워크를 낭비한 게 되긴 하지만 사용될 경우 사용자의 체감 속도를 빠르게 할 수 있다.
- 네가 메뉴를 누른는 순간에 파일들이 내려가고 통신으로 다운받는 시간, 번역해서 이해하는 시간이 다 필요하니까 니가 누를때 받는게 아니라 미리 다운받고 번역해놓으면 바로 실행할 수 있지 않겠니?
✅ 서버 푸시 - 동작
- promised : 미리 보내고 싶은 컨텐츠의 헤더를 내릴게 / 서버에서 클라이언트에게 내가 이 컨텐츠를 너에게 보내고 싶은데 이게 미리 보내는 콘텐츠의 헤더야!
- 서버의 push를 Client가 무조건 받아야하는 것이 아니다. 이동통신은 서버와 client 사이 주고 받는 컨텐츠에 대한 돈을 client가 내기 때문에 동의 없이 마구잡이로 컨텐츠를 뿜어서 이동통신을 겪으면 요금제 폭탄~
- 서버는 제한을 해요 헤더를 보내서 니가 이걸 받았으면 좋겟어~하지만 client 거절 쌉가능. 니가 push한거 내 cash에 이미 들어있는뎅? 이미 갖고있으니까 안받아도 됑 = 거절
- 너의 제안 좋아 / push 받을 의사가 있는데 동시다발적으로 보낼 수 있는 stream의 개수가 n개면 좋겠어. = client의 결정 권한
- 보안은 TCP 레벨에서 이루어지기 때문에 한 번 TCP를 뚫고 나면 그 안에서 새로운 Req/Res가 와도 암호화 관련된 추가적인 작업은 필요가 없다. 성능 저하가 되지 않는다.
✅ 서버 푸시 현황 (2021.09)
- 서버 푸시는 이동통신과 친하기 힘들다.
- multiplexing이 이론적으로는 좋지만 트리같은거 구현하려면 전공지식이 엄청나야함. 마찬가지로 서버 푸시도 어렵다.
- 2020년 크롬에서 http2 서버푸시 삭제 / 크롬에서 빠졌다는거지 http2에서 사라졌다는 것이 아님.
- 빠진 이유 : 설문조사햇더니 0.05%만 서버푸시 사용 중이었음.
🍏 HTTP/2 - 헤더 압축
- 컨텐츠 압축 X (이건 어플리케이션이 해야할 일) 헤더 압축 O
- HTTP/1.1에서 req/res의 Header가 500~800 byte라는 어마어마한 사이즈
- Cookie의 사이즈도 수십 KB까지 늘어났다.
- 내가 주고 받아야 할 애들의 크기보다 헤더나 쿠키의 크기가 훨씬 커져버렸다.
- HTTP/2 = HPACK 등장
✅ HTTP/2 - HPACK
= 헤더 압축 방식
- 과거와 현재의 차이 기반 압축
- 동일한 Stream 안에서 앞에 있는 HTTP Req/Res의 Header 정보가 바뀌지 않았다면 다시 보내지 않는다. 달라진 정보만 다음 메세지에서 보낸다.
- 장점: 정보량을 줄일 수 있다.
- 단점 : processing power가 늘어난다.
Huffman coding
- 중복되지 않은 Header 정보를 Huffman 코덱 방식을 사용해서 가능한 소량의 정보로 사이즈를 줄인다.
Indexed List
- Table을 이용해 정보를 index로 대체해서 보낸다.
- 헤더에 중복값이 존재하는 경우 Header Table 개념을 사용하여 중복 헤더를 검출하고, 중복된 헤더는 index 값만 전송한다.
✅ HTTP/2의 헤더 압축 예제
- 동일한 Stream에서 발생한 req 두개
- 뒤 req에서 달라진 path 정보만 전송한다
- Table based로 정보를 줄인다.
- Static table : 미리 만들어놓은 테이블
굳이 매번 주고받지 않을 수 있도록 반드시 주고받는 글자들 (get/put/post 등)을 오른쪽에 쓰고 왼쪽에 index를 매긴다.
- Dynamic Table : 서비스를 시작하는 시점부터 만들어지는 field정보(통신을 끊기 전까지 바뀌지 않는)를 dynamic으로 담는다.
- 이 테이블을 서버와 클라이언트 모두 가진다. 첫번째는 통으로 보내지만 두번째 req부터는 index를 보낸다.
- 중복값이 존재하는 경우 index만 보냄으로써 메세지 크기를 효과적으로 줄일 수 있다.
- 중복되지 않은 값들은 기존 Huffman 코덱 방식으로 사용해서 가능한 소량의 정보로 사이즈를 줄인다.
- 값이 바뀌었을 경우 기존 index + 바뀐 값을 pair로 전송
🍏 [FYI] RSA Encryption
- 암호화 Key / 복호화 Key -> 두 개의 Key 사용
- 이 두개가 다른 것을 비대칭성이라 한다
- Server : 두개의 Key를 만들고 암호화 Key(Public)를 Client에게 전달
- Client : 암호화 키(Public)로 암호화해서 보내면 서버는 받은 정보를 본인의 복호화 Key(Private)로 해석
- Client-Server는 암호화를 통해 신뢰성있는 보안 통신을 할 수 있게 된다.
- 암호화 Key = Public key
- ex) 서버 인증서
- 브라우저에서 인증서가 없습니다~ 하는 거
= 암호화 Key 없으니까 암호화 못해. 서버에게 보내는 정보가 누군가한테 보여질 수 있는데 그래도 보내겠니?
- 복호화 Key = Private Key (개인에게만)
- 공개키로 그 누구나 암호화를 할 수 있지만 개인키를 가지고 있지 않으면 아무나 복호화를 할 수 없다.