gRPC 그리고 HTTP/2

2022. 9. 4. 15:38gRPC

 

 

 

Overview

gRPC는 HTTP/2의 long-lived Connection을 기반으로 작동합니다. (이로 인해 HTTP/1을 사용해 통신하는 Web에서 gRPC를 사용해서 개발하기 위해서는 fallback으로 REST를 사용하거나 중간에서 HTTP/1 요청을 받아 HTTP/2로 넘겨주는 Proxy가 필요합니다.) 이를 이해하고 사용하기 위해서는 HTTP/1과 HTTP/2가 어떻게 다른지, 그리고 왜 gRPC가 HTTP/1이 아닌 HTTP/2를 기반으로 작동하는지에 대해 이해해야 합니다. 이번 포스팅에서는 이에 대해 살펴보도록 하겠습니다.

 

이 포스팅은 gRPC가 무엇인지에 대한 간략한 내용을 이해하고 있다는 가정 하에서 작성되었습니다. gRPC에 대한 자세한 내용은 아래 공식 문서를 참고해 주세요. (이후 포스팅에서 gRPC-Web에 대한 내용들도 다룰 예정입니다.)

 

About gRPC

Who is using gRPC and why

grpc.io

 

 

HTTP/1 vs HTTP/2

오늘날 대부분의 웹사이트들은 HTTP/1.1 프로토콜 위에서 동작합니다. 이 HTTP/1.1 프로토콜은 1999년 6월 처음 소개되었고 지금까지도 잘 사용되고 있으나, 몇몇 영역에서 성능상의 한계를 보여주었고 이에 따라 HTTP/1.1의 성능 향상에 초점을 맞추어 개선된 HTTP/2.0 프로토콜이 2015년 소개되었습니다. HTTP/2.0은 이전의 프로토콜에서 제공하지 않았던 Semantic Mapping, Concurrency 등의 기능을 제공함으로써, Network Latency와 HOL(Head Of Line) Blocking 등을 개선하였고, 이 모든 개선사항들의 중심에는 "Binary Framing Layer"가 있습니다.

HTTP/2, whose specification was published in May of 2015, seeks to address some of the scalability concerns of its predecessor while still providing a similar experience to users. HTTP/2 improves upon HTTP/1.1’s design in a number of ways, perhaps most significantly in providing a semantic mapping over connections  -cncf

 

Binary Framing Layer

The ability to break down an HTTP message into independent frames, interleave them, and then reassemble them on the other end is the single most important enhancement of HTTP/2 - web.dev

 

Binary Framing Layer는 HTTP/2 성능 개선의 핵심입니다. HTTP/1.1 프로토콜에서 하나의 요청과 응답이 Text Object로 전송되었다면 HTTP/2에서는 이를 보다 잘게 쪼갠 Binary Frame 단위로 전송하게 됩니다. 즉, 서버와 클라이언트 사이의 데이터 전송 방식을 Text에서 Binary로 바꾸고, 전송되는 메시지를 조금 더 작은 단위로 나누어 전송한다는 이야기인데, HTTP/2는 이를 설명하기 위해 다음 3가지 용어(Terminology)를 소개합니다. 

 

Stream

A bidirectional flow of bytes within an established connection, which may carry one or more messages.

 

Stream은 하나 이상의 메시지들을 전송할 수 있는 양방향의(bi-directional) 커넥션을 의미합니다. 하나의 HTTP/2 커넥션(TCP 커넥션) 안에는 여러 개의 양방향 스트림을 생성할 수 있으며, 각각의 스트림은 고유한 식별자(unique identifier)와 우선순위에 대한 정보를 갖습니다. 이렇게 하나의 커넥션에 여러 스트림을 가질 수 있도록 허용함으로써, HTTP/2 프로토콜은 우선순위에 따른 Concurrency와 Semantic Mapping이 가능하게 도와줍니다.

 

하나의 커넥션 안에 다수의 bi-directional stream이 존재할 수 있다.

 

Message

A complete sequence of frames that map to a logical request or response message.

 

Message는 아래에서 소개할 Frame들의 완전한 조합으로 하나의 논리적인 Request나 Response에 대응됩니다. 각각의 메시지는 Logical HTTP Message를 의미하며, 하나의 Stream 안에서 전송됩니다.

 

 

Frame

The smallest unit of communication in HTTP/2, each containing a frame header, which at a minimum identifies the stream to which the frame belongs.

 

Frame은 HTTP/2 프로토콜에서 전송되는 가장 작은 단위의 데이터 조각이며, 각각의 프레임은 fream header를 포함합니다. frame header에는 각 frame이 속한 Stream에 대한 정보가 포함됩니다.

 

 

frame들의 조합으로 message를 구성하며, message는 stream 내에서 전송된다

 

 

 

Semantic Mapping

HTTP/2 takes the concept of persistent connections further by providing a semantic layer above connections: 
streams. Streams can be thought of as a series of semantically connected messages, called frames. A stream may be short-lived, such as a unary stream that requests the status of a user (in HTTP/1.1, this might equate to `GET /users/1234/status`). With increasing frequency it’s long-lived. -cncf

 

HTTP/2.0은 하나의 TCP 커넥션을 여러 개의 bi-directional Stream이 사용할 수 있도록 합니다. 기존에는 "/api/users/me" 엔드포인트에 요청을 보내서 사용자 정보를 가져오기 위해서 매번 TCP 커넥션을 생성하고, TLS 설정을 맺어주고, Header과 기타 설정들을 보내야 했습니다.(Several Handshakes) 하지만 HTTP/2.0에서는 하나의 오리진(origin)에 대한 커넥션을 유지한 채로, 여러 개의 Stream을 통해 데이터를 주고받기 때문에 최초로 클라이언트와 서버가 TCP 커넥션을 맺었다면, 이 커넥션 안에서 서로 다른 메시지들이 다른 Stream을 통해서 양방향으로 오갈 수 있게 됩니다. 즉, "/api/user/me"를 한번 요청해서 TCP 커넥션을 맺었다면, 이후 동일한 오리진에 대해 연속적인 요청을 보낼 때, TCP, TLS 관련 설정을 위한 시간을 소비하지 않고 새로운 Stream을 만들어서 지속적으로 요청을 보낼 수 있다는 의미입니다. 

 

위에서 살펴보았듯이, Stream안에서 전송되는 Message는 Frame이라는 바이너리 데이터 조각으로 구성되며, 각 Frame은 자신이 속한 Stream을 유일하게 식별할 수 있는 정보를 Header에 포함하고 있기 때문에 하나의 커넥션에서 여러개의 Stream이 여러 Frame을 보내도, 이들을 유일하게 식별하고 각각의 메시지로 조합해서 이해할 수 있게 됩니다. 이를 Semantic Mapping이라 합니다.

 

 

 

Concurrency

The primary advantage of streams is connection concurrency,
i.e. the ability to interleave messages on a single connection. - cncf

 

HTTP/2.0의 Stream을 사용하면, 여러개의 메시지들이 하나의 커넥션에서 동시성(Concurrency)을 가지고 전송될 수 있습니다. 예를 들어 Service A가 Service B와 HTTP/2 커넥션을 만들고 3개의 Stream("new users", "profile updates", "product order")을 생성했다고 해보겠습니다. HTTP/1 커넥션의 경우 하나의 커넥션에서 하나의 메시지만 주고받을 수 있기 때문에 "new users"에 대한 응답이 처리된 이후에야 "profile updates"를 처리할 수 있을 것입니다. (이를 HOL(Head Of Line) Blocking이라고 합니다.)

 

하지만 HTTP/2를 사용했기 때문에 서로 다른 Stream의 메세지들은 동시성을 가지고(3개가 동시에 전송되는 것이 아닌 "동시에 전송되는 것처럼 보이는"것임에 주의해야 합니다. Concurrency는 본질적으로 Parallelism과는 다르며, 커넥션은 여전히 하나입니다.) 전송되며, 이는 다음 요청을 보내기 위해 첫 요청이 완료된 필요가 없다는 것을 의미합니다. HTTP/2가 이렇게 Concurrency를 지원하는 것을 "Multiplexing"이라고 부릅니다.

 

 

 

 

 

 

gRPC Semantics & HTTP2

지금까지 HTTP/2가 HTTP/1.1 과 무엇이 다르며, 어떤 원리를 가지고 동작하는지에 대해 간단하게 살펴보았습니다. 이번에는 HTTP/2에 대해 이해한 내용을 바탕으로 HTTP/2를 사용해서 통신하는 gRPC의 Semantics에 대해 살펴보도록 하겠습니다.

In this article, we’ll look at how gRPC builds on HTTP/2’s long-lived connections to create a performant, robust platform for inter-service communication. We will explore the relationship between gRPC and HTTP/2, how gRPC manages HTTP/2 connections, and how gRPC uses HTTP/2 to keep connections alive, healthy, and utilized.

 

 

Channels = Connection, Streams = RPCs, Messages = Data Frame

 

 

 

공식문서에 따르면, gRPC는 3개의 개념을 기반으로 구성됩니다. Channels, Remote Procedure Calls(RPCs), 그리고 Message인데, 이 3개의 개념은 의미상 HTTP/2 프로토콜을 구성하는 요소였던 TCP Connection, Streams, Data Frames과 의미상으로 대응됩니다. 각각의 의미상의 대응을 공식문서의 설명을 통해 살펴보도록 하겠습니다.

 

 

 

 

Channels represent virtual connections to an endpoint, which in reality may be backed by many HTTP/2 connections. RPCs are associated with a connection (this association is described further on). RPCs are in practice plain HTTP/2 streams.

 

Channel은 실제로 여러 개의 HTTP/2 Connection 위에서 생성되지만, 하나의 커넥션 위에서 여러개의 Stream을 사용해 메시지를 처리하는 원리는 동일합니다. RPC는 실제로 HTTP/2 Stream에 해당되며, Channel에서 하나의 RPC 콜을 호출한다는 것은 HTTP/2 Connection 위에서 Stream을 만드는 것을 의미합니다. RPC 콜이 종료되면 해당 Stream도 종료됩니다.

 

Messages are associated with RPCs and get sent as HTTP/2 data frames. To be more specific, messages are layered on top of data frames. A data frame may have many gRPC messages, or if a gRPC message is quite large it might span multiple data frames.

 

RPC 콜을 통해 주고받는 메시지들은 기본적으로 HTTP/2 data frame이며, 메시지의 크기가 큰 경우에는 data frame들의 묶음(chunk) 형태로 구성됩니다. 즉, 하나의 Channel에서 RPC콜을 통해 메시지를 전송하는 과정과 HTTP/2 커넥션에서 특정 Stream을 통해 Data Frame이 전송되는 것은 의미상으로 동일한 것입니다.

 

Conclusion

gRPC와 HTTP/2 프로토콜이 갖는 이러한 의미상의 관계로 인해, HTTP/1.1을 지원하는 브라우저에서는 gRPC를 사용해 직접적으로 통신을 할 수 없습니다. 이를 지원하기 위해 grpc 재단에서는 브라우저와 서버 사이에 envoy proxy를 두어 문제를 해결하려고 한 grpc-web을 선보였으며, 아예 gapic-generator-typescript와 같은 라이브러리를 사용해서 브라우저와 서버 간의 통신에는 REST를 사용하는 방법도 알려져 있습니다.

 

grpc-web 개념도

 

 

근본적으로 마이크로서비스 아키텍쳐에서 gRPC를 사용하는 가장 큰 목적 중 하나가 서로 다른 언어로 개발된 여러 서비스들 간의 공통 인터페이스(profobuf IDL)를 통해 통신하는 것이라고 했을 때, HTTP/2 기반 위에서 생겨난 gRPC가 HTTP/1.1에 상당부분 의존하고 있는 Web Platform에 대한 Full-Support를 제공하기 위한 여러 방법들을 마련해 줄 것으로 생각됩니다.

 

 

Reference

https://www.cncf.io/blog/2018/07/03/http-2-smarter-at-scale/

https://grpc.io/blog/grpc-stacks/

https://web.dev/performance-http2/

https://blog.restcase.com/http2-benefits-for-rest-apis/

https://grpc.io/blog/grpc-on-http2/

반응형