RPC 알아보기 2 - Protocol Buffer와 HTTP/2.0
Intro
지난 RPC 게시물에 이어서, 이번에는 Google에서 만든 gRPC의 베이스 개념을 정리하고자 한다.
gRPC는 직렬화 구조로 Protocol Buffer을 사용하며, 통신 프로토콜로는 HTTP/2를 사용한다.
Protocol Buffer
프로토콜 버퍼(Protocol Buffer, protobuf)는 직렬화 데이터 구조이다. 직렬화(serialization)란, 데이터를 0과 1인 바이트 비트(bit)로 표현하는 것을 의미한다. 예를들어, UTF-8에서는 “hello”라는 문자 데이터를 인코딩 후 16진수로 표현하면 68656c6c6f 이다.
객체 직렬화도 존재한다. Person이라는 객체에는 name, age가 존재한다고 하자. 우리는 특정 객체를 {"name" : "hong", "age" : 25}
처럼 json 형식으로 나타낼 수 있다.
특징
JSON은 사람들이 보기에 직관적이다. 하지만 데이터 크기가 크다는 단점이 존재한다.
1
2
3
4
5
{
"userName" : "Martin",
"favouriteNumber" : 1337,
"interests" : ["daydreaming","hacking"]
}
위의 데이터 크기는 (공백을 제외하고) 82 Bytes 이다. 같은 객체를 Protocol Buffer을 사용해서 표현하면 아래와 같이 33 Bytes만 사용한다. https://martin.kleppmann.com/2012/12/05/schema-evolution-in-avro-protocol-buffers-thrift.html 불필요한 문자열 대신에 숫자로 대체해서 표현하는게 핵심이다.
장단점
가장 큰 장점은 데이터 크기가 작다는 것이다. 따라서 네트워크를 통한 통신에서 많은 속도 이점을 얻을 수 있다. 단점으로는 사람이 읽기 불편하다는 점이다. 프로토콜 버퍼를 사용하는 기기에는 proto 파일이 있어야 직렬화, 역직렬화를 수행할 수 있다. 그래서 주로 내부 서버끼리 통신할 때 사용한다. https://www.researchgate.net/figure/To-use-Protocol-Buffers-it-is-necessary-to-generate-code-for-each-message-that-needs_fig17_285578991
HTTP/2.0
HTTP/1.1 단점
HTTP/1.0은 TCP 커넥션당 한번의 요청과 응답만 가능하다. 이는 불필요한 TCP 커넥션을 만들게된다. 예를들어, 어떤 사이트에 접속하여 온전하게 사용자에게 보여주려면 HTML, 이미지, JS, CSS 등을 서버에서 가져와야하는데 계속 커넥션을 새롭게 맺는다면 오버헤드가 커진다.
이를 해결하고자 HTTP/1.1에서는 Persistent Connection을 도입했다. 지정한 timeout 동안에는 커넥션을 닫지 않는 방식이다. 또한 요청에 대한 응답을 받지 않아도, 여러 요청을 연속적으로 보낼 수 있는 pipelining 기법이 추가되었다.
https://developer.mozilla.org/ko/docs/Web/HTTP/Connection_management_in_HTTP_1.x
하지만 응답을 반드시 요청 순서에 맞게 받아야 한다는 단점이 있었다. 이는 HOL(Head of Line) 블로킹 문제를 야기했다. 이를 해결하기 위해 병렬 TCP 연결을 사용하는 방법도 있지만, 커넥션을 여러번 맺어야 하고 복잡하다는 단점이 있다.
중복 헤더의 문제도 존재한다. 하나의 커넥션에 보내는 다수의 요청은 Header가 거의 비슷한 경우가 많다. 그럼에도 매 요청마다 전체 헤더를 보내야하므로 요청 메시지가 커진다.
HTTP/2
2015년에 HTTP/2가 등장했다. 기존의 HTTP/1.X 버전의 성능을 향상시키기 위한 확장판이다. 지금은 gRPC를 위한 사전 지식이므로 간단하게 특징만 정리하고자 한다.
- 헤더와 바디를 frame으로 분리 및 바이너리로 변환
- request/response의 단위는 message이며, message는 여러 frame으로 이루어졌다.
- text를 바이너리로 인코딩하여 전송한다.
- 하나의 커넥션에 여러 개의 스트림이 동시에 열릴 수 있다.
- 스트림마다 31비트의 정수로 된 고유한 식별자를 갖는다.
- 각 스트림에 우선 순위를 지정할 수 있다.
- 위 특징들을 이용해 HOL 블로킹을 피할 수 있다.
- 각 객체를 작은 프레임으로 나누고
- 하나의 TCP 연결을 통해 객체들을 이루는 작은 프레임을 객체별로 한 개씩 보낸다.
- 예를 들어 비디오 객체가 1000프레임, 작은 객체가 2프레임으로 이루어져 있다하면
- 기존 HTTP/1.1 방식에서 작은 객체는 1002 프레임이 보내져야 전송이 완료되지만
- HTTP/2 방식에서 작은 객체는 4프레임이 보내지면 전송이 완료된다. (사실 상황에 따라 다르다.)
- Server Push
- 클라이언트가 요청하지 않았던 리소스를 서버가 보낼 수 있다.
- Compression header