콘텐츠로 건너뛰기

What is gRPC?

Photo by Hayden Scott on Unsplash

Intro

grpc를 이용하여 몇 개의 프로젝트를 했음에도 불구하고 아직 gRPC가 어떤 것인지 명확하게 머리 속에 자리잡지 않은 것 같아서 gRPC 개념에 대해서 정리해보려고한다.

gRPC의 개념에 대해서 한 문장으로 정리하자면 HTTP/2 기반의 RPC 프로토콜이라고 할 수 있다. 그래서 이 개념에 대해서 이해하기 위해서는 RPCHTTP/2 프로토콜 대해서 먼저 알아야한다.

RPC

RPC는 Remote Procedure Call의 약자로 의미 그대로 원격 컴퓨터나 프로세스에 존재하는 함수를 호출하는 프로토콜 이름이다. 기존 로컬 주소 공간에 존재하는 함수(procedure)를 호출하는 것을 확장하여 다른 주소 공간에 존재하는 함수(procedure)를 호출할 수 있는 프로토콜이다. 여기서 다른 주소 공간이라함은 같은 머신 내의 다른 프로세스를 말할 수도 있고 네트워크가 연결되어 있는 다른 머신의 프로세스를 말할 수도 있다. 그렇기 때문에 RPC로 클라이언트-서버 구조의 어플리케이션을 만들 수 있다

RPC는 일반적으로 request parameter과 response parameter를 알아야한다. 그렇기 때문에 미리 서버와 클라이언트 양쪽의 인터페이스를 맞춰야 하는데 Interface Definition Language라고 부르는 IDL로 함수명, 인자, 반환 값에 대해서 정의한 뒤 rpcgen 컴파일러를 이용하여 stub 코드를 자동 생성한다. gRPC는 자신만의 rpcgen 컴파일러가 존재하는데 이는 뒤에서 살펴보겠다.

이 때 생성되는 stub 코드는 개발자들에게 친숙한 go, java와 같은 원시소스코드 형태로 만들어지고 클라이언트, 서버 프로그램에 포함하여 빌드한다.

mechanism

gRPC mechanism

RPC 프로토콜은 다음과 같은 과정을 거쳐서 이뤄진다.

1. 클라이언트는 client stub procedure를 호출하는데 호출하는 입장에서는 평소 로컬 함수를 호출하듯이 함수 인자를 함수에 전달한다. client stub은 클라이언트 주소 공간에 존재한다.

2. client stub은 전달받은 인자들을 marshal(pack)하여 메세지를 만든다. marshal하는 과정에서 데이터형을 XDR(eXternal Data Representation) 형식으로 변환하여 바꾼 인자들을 메세지에 포함시킨다. 이렇게 XDR로 변환하는 이유는 integer, float과 같은 기본 데이터 타입에 대해서 머신마다 메모리 저장 방식(i.e. little endian, big endian)이 CPU 아키텍처 별로 다르며, 네트워크 전송 과정에서 바이트 전송 순서를 보장하기 위해서이다.

3. 이렇게 marshaling을 거친 메세지들은 transport layer에 전달하는데 transport layer은 리모트 서버 머신에 메세지를 전달한다.

4. 서버에서는 전달받은 메세지를 server stub에 전달하고 unmarshal(unpack) 과정을 거처서 클라이언트가 전송한 인자들을 얻어내고 클라이언트가 호출하려고하는 함수를 서버에서 찾아내어 서버의 함수로 실행시킨다. 실행 결과는 다시 server stub에 전달되고 클라이언트가 서버에 함수 인자를 전달하는 방식과 마찬가지로 결과 값이 클라이언트에 반환된다.

RPC라는 개념이 존재하기 전부터 소켓 프로그래밍을 통해서 네트워크에 연결되어있는 다른 서비스를 호출할 수는 있다. 그렇지만 네트워크를 타는 통신은 서버가 응답하지 않는 등의 장애가 발생할 수 있다. 이때 소켓 프로그래밍으로 구현했다면 이와같은 장애에 대해서 개발자가 직접 핸들링해야 한다.

RPC는 이러한 번거로움을 덜어준다. 네트워크 통신과 같은 번거로운 로직들을 감추고 추상화된 통신 인터페이스를 제공해준다. 또한 HTTP 기반의 REST가 유행하면서 RPC는 많이 사라졌는데, REST의 경우 request parameter와 response 값이 명시적이지 않기 때문에 오류가 생길 여지가 있고 JSON을 HTTP를 통해서 쏘기 때문에 속도가 비교적 떨어진다는 단점을 가지고 있다.

Protocol Buffers

gRPC는 protobuf를 IDL로 사용할 수 있다. Protobuf는 구글에서 만들고 현재 사용되고 있는 데이터 직렬화 라이브러리다. protobuf 파일의 예시는 아래와 같다.

// Person.proto
message Person {
  string name = 1;
  int32 id = 2;
  bool has_ponycopter = 3;
}

IDL로 정의된 메세지를 기반으로 데이터 접근을 위한 코드를 생성하기 위해서 protoc 컴파일러를 사용한다. 간단한 명령어로 개발자가 원하는 프로그래밍 언어로 stub 코드를 생성할 수 있고 getter/setter 등 서버, 클라이언트 인터페이스가 자동 생성된다.

HTTP/2

HTTP/1은 기본적으로 클라이언트가 서버에 요청을 보내고, 서버가 요청에 대한 응답을 다시 보내는 구조이다. 그렇기 때문에 클라이언트가 서버에 요청을 할 때마다 커넥션이 맺어지고 서버를 왕복해야한다. 또한 헤더의 크기는 불필요하게 큰 편이다. 이와 같은 이유로 HTTP/1은 느리다.

구글에서는 이러한 문제점을 해결하기 위해서 SPDY를 만들었고 이를 기반으로 HTTP/2 표준이 만들어졌다.

SPDY

과거에 비해 오늘날에는 훨씬 더 많은 리소스를 주고 받고 있으며 보안이 훨씬 중요한 이슈가 되고 있다. 이렇게 바뀐 환경을 고려하여 구글에서는 느린 HTTP를 보완한 SPDY 프로토콜을 발표했다. SPDY 프로토콜은 latency 문제를 주로 해결하고자 하였다.

SPDY 프로토콜의 특징은 다음과 같은 것들이 있다.

항상 TLS(Transport Layer Security) 위에서 동작

따라서 HTTPS로 작성된 웹에서만 적용 가능하다.

Header Compression

HTTP 통신을 할 때 메세지에 포함되는 HTTP 헤더에는 요청마다 반복되는 내용들이 매우 많다. 그렇기 때문에 헤더 압축만으로도 리소스를 엄청 절약할 수 있다. 구글 발표 자료에 따르면 HTTP 헤더 압축을 할 경우, 최초 요청 시에는 압축을 안했을 때에 비해 10~30%를 줄일 수 있고 long-lived connection과 같이 여러 번 요청을 주고 받을 경우에는 80~97%까지 헤더 크기를 감소시킬 수 있다.

Binary protocol

HTTP/1.1에 존재하지 않았던 프레임이라는 새로운 단위가 생겼는데 이는 통신상의 제일 작은 정보 단위를 말한다. SPDY 프로토콜은 프레임을 텍스트가 아닌 바이너리로 구성하여 파싱이 더 빠르고 오류 발생 가능성이 낮다.

Multiplexing

HTTP에서는 하나의 커넥션에서 한 번에 하나의 요청을 처리하며 요청에 대한 응답이 순차적으로 이뤄졌다. 이와 달리 SPDY에서는 하나의 커넥션 안에서 여러 개의 독립적인 스트림을 동시에 처리한다. 그렇기 때문에 적은 수의 커넥션으로 다수의 요청, 응답을 동시에 처리할 수 있다. 또한 HTTP에서는 요청들이 FIFO로 처리되기 때문에 하나의 요청에 대한 응답이 지연되면 나머지 응답도 모두 늦어지는 HTTP pipelining과는 달리 SPDY에서는 각각의 요청, 응답이 모두 독립적으로 처리된다.

Full-duplex interleaving, stream priority

스트림이 진행 중일 때 다른 스트림이 끼어드는 것을 허용하고 스트림에 대해서 우선 순위 설정도 가능하기 때문에 우선 순위가 낮은 데이터를 전송 중일 때 우선 순위가 높은 데이터가 끼어들어서 더 빨리 전달할 수 있다.

Server push

Long-polling과는 다르게 클라이언트의 요청이 없어도 서버에서 컨텐츠를 직접 push할 수 있으며 리소스 캐싱이 가능하다. 하지만 server push를 구현하기 위해서는 어플리케이션 로직에 추가 구현이 필요하다.

Server push와 같은 경우를 제외하면 SPDY를 적용하기 위해서 추가적으로 작업해야할 필요는 없다. 한 가지, 브라우저와 서버가 SPDY를 지원해야한다. 그리고 spdy:// 와 같이 프로토콜 scheme이 없고 브라우저에서도 SPDY 프로토콜 사용 여부에 대해서 어떤 표시도 하지 않기 때문에 사용자는 굳이 SPDY 사용여부를 알 필요 없이 투명하게 적용 가능하다.

HTTP/2는 SPDY를 기반으로 만들어졌기 때문에 위에서 언급했던 SPDY의 특징을 가지고 있다.

gRPC

gRPC는 HTTP/2 특징을 기반으로 하는 RPC 프로토콜이다. 그렇기 때문에 양방향 스트리밍이 가능하고 HTTP보다 통신 속도가 빠르다. 그리고 RPC의 특징처럼 네트워크 통신에 대한 추상화를 제공해주어서 사용자가 네트워크 프로그래밍에 대해 신경을 쓰지 않게 되고 비즈니스 로직 개발에 집중할 수 있도록 도와준다.

그리고 과거에 gRPC의 단점 중 하나였던 gRPC에 대해 api-gateway 도입이 어렵다는 것이 있었는데 현재는 grpc-gateway 프로젝트를 통해 이는 극복한 것 같다.

필요한 코드의 양이 적고 여러 프로그래밍 언어를 지원한다는 장점이 있지만 프로토콜의 한계상 데이터 포맷 변환이 런타임에 자유롭지 못하다는 단점이 있다.

Reference

  • https://medium.com/@goinhacker/microservices-with-grpc-d504133d191d
  • https://www.tutorialspoint.com/remote-procedure-call-rpc
  • https://www.geeksforgeeks.org/operating-system-remote-procedure-call-rpc/
  • https://leejonggun.tistory.com/9
  • https://d2.naver.com/helloworld/140351
  • https://www.chromium.org/spdy/spdy-whitepaper/
  • https://grpc.io/docs/guides/
  • https://grpc.io/docs/guides/concepts/

Leave a comment