네트워크

gRPC basic concepts

teo_99 2024. 5. 22. 01:37

Remote Procedure Call

gRPC는 구글에서 개발한 프레임워크다. 따라서 앞에 'g'가 붙은 것임을 유추할 수 있을 것이다. 그렇다면 RPC는 무엇인가? gRPC라는 개념을 알아보기에 앞서 RPC(Remote Procedure Call)에 대해 이해해보자.

 

RPC는 직역하면 원격 프로시저 호출로, 다른 주소 공간에서 함수나 프로시저를 실행할 수 있게 하는 프로세스 간 통신 기술이다. 쉽게 말해, 다른 컴퓨터나 프로세스에 있는 함수를 마치 내 것인것 마냥 원격으로 호출할 수 있다는 것이다. 

 

기본적으로 RPC는 네트워크 기반으로 동작하도록 설계되었다. 즉, 원격으로 내 컴퓨터에 있는 다른 프로세스의 함수를 호출하는 것이 1순위 목적이 아니라, 네트워크를 타고 다른 노드에 있는 함수를 호출하는 것이 목적이라는 것이다.

https://jindongpu.wordpress.com/2013/01/30/remote-procedure-call-sequence-of-events/

 

 

Why RPC?

참고로, gRPC가 최신 기술인 것에 반해 RPC는 1980년대에 처음으로 제안되었다. (논문 참고: 링크) Xerox라는 연구소에서 해당 아이디어를 제안했는데, Xerox는 GUI, Ethernet을 처음으로 고안한 회사이기도 하다. 

 

그럼 왜 RPC가 등장했는가? 바로 분산 환경에 대한 니즈 때문이다. 이전에는 메인프레임이라는 컴퓨터를 통해 연산을 처리했는데, 기업 입장에서는 값비싼 메인프레임보다는 비교적 저가의 워크스테이션 서버 여러 대로 서비스를 제공하고자 하게 되었다.

 

이 과정에서 '워크스테이션 간 어떻게 통신할 것인가'가 자연스레 화두에 올랐고, 네트워크의 중요성도 대두된다. 다만 네트워크는 상당히 저수준(low-level)이었기 때문에, 소켓 프로그래밍, 메시지 직렬화/역직렬화, 오류 처리 등과 같은 번거로운 작업들을 처리해야 했다. 그리고 이러한 문제를 해결하기 위해 RPC가 등장한 것이다. 

 

다음은 RPC의 논문에서 발췌한 내용을 번역한 것인데, 참고하면 좋을 듯 하다.

이 아이디어에는 여러 가지 매력적인 측면이 있습니다. 하나는 깔끔하고 단순한 의미론입니다. 이를 통해 분산 계산을 더 쉽게 구축하고 올바르게 수행할 수 있습니다. 또 하나는 효율성입니다. 프로시저 호출이 간단해 보이므로 통신이 매우 빨라집니다. 세 번째는 일반성입니다. 싱글 머신 계산에서 프로시저는 알고리즘의 각 부분 간의 통신을 위한 가장 중요한 메커니즘인 경우가 많습니다.

 

그렇다면 이렇게 오래 된 기술인 RPC가, 왜 최근 들어 gRPC등과 같은 프레임워크들이 등장했는지에 대해 의문이 생길 수 밖에 없다. 이는 여러 기술적 발전이 있었기 때문이다. 네트워크 분야에서는 대역폭이 크게 증가했고, 마이크로서비스와 같은 아키텍쳐가 등장하면서 이전 기술인 RPC에 다시 이목이 집중되었다.

 

이와 같은 RPC에는 여러 구현 모델이 존재한다. Facebook이 만든 Thrift, Twitter이 만든 Finagle 등이 대표적인 구현체이다. 그 중 우리는 Google이 만든, gRPC에 대해 살펴보도록 하자.

 

Protocol Buffer and gRPC

앞서 RPC란 '원격지의 함수나 프로시저를 네트워크를 통해 호출하는 기술' 이라고 설명했다. 그렇다면 RPC의 구현체들에는 이러한 기능을 가능하게 하기 위한 방법론들이 존재할 것임을 자연스레 유추해볼 수 있다.

 

gRPC에서는 Protocol Buffer를 IDL(Interface Definition Language)로, 그리고 기본 메세지 교환 형식으로 사용함으로서 이러한 기능을 가능하게 한다. IDL이란, 인터페이스 정의 언어로 일종의 인터페이스를 묘사하기 위한 언어라고 생각하면 된다.

 

원격지의 함수를 마치 내 것인마냥 호출하기 위해선, 타겟 시스템의 환경이나 운영체제, 메세지 형식 등에 구애받지 않아야 한다. 즉, '언어'를 통일할 필요가 있는 것이다. 그리고 Protocol Buffer가 IDL로서 이러한 역할을 한다. 우선 Protocol Buffer를 조금 알아보고, gRPC에서 어떻게 이를 사용해 통신하는지 알아보자.

 

Protocol Buffer

Protocol Buffer 역시도 구글이 만들었는데, gRPC보다 약 8년 빠르게 공개된 기술이다. 공식 문서에 따르면 Protocol Buffer는 다음과 같은 역할을 한다.

Protocol Buffers are a language-neutral, platform-neutral extensible mechanism for serializing structured data.

 

 

즉, 언어나 플랫폼과는 상관 없이 구조화된 데이터를 직렬화할 수 있는 확장 가능한 방식이라는 의미다. 쉽게 말해 타겟 서버의 프로세스가 사용하는 언어나, OS 등과는 상관 없이 메세지를 주고받을 수 있는 방식이다. JSON과 비슷한 형태라고 이해해도 좋을 듯 하다.

 

조금 더 구체적으로는 아래와 같이 메세지 형태를 '.proto' 파일에 정의하는 방식이다. 이렇게 메세지 형식을 정의하면, Protocol Buffer 컴파일러를 통해 특정 언어를 지정하고 컴파일할 수 있다. 그러면 각 언어에 맞는 코드가 자동으로 생성되고, 이후 해당 코드를 사용하면 된다.

message Person {
  optional string name = 1;
  optional int32 id = 2;
  optional string email = 3;
}
// Java로 Compile하는 예시
protoc --java_out=. person.proto

 

이러한 Protocol Buffer는 중요한 특성이 있는데, 바이너리 형태로 데이터를 전송한다는 점이다. 이는 JSON과는 확실히 대조적이다. 가령 아래와 같은 JSON이 있다고 가정해보자.

 

JSON은 바이너리 형태가 아닌 문자 그 자체로 직렬화되어 전송된다. 즉, 각각의 정보를 Human Readable한 문자열 그 자체로 전송하기 때문에 전송하는 메세지의 용량이 커질 수 밖에 없다. 위 JSON의 경우, whitespace를 제외하고 82바이트를 차지한다.

binary encoding in Protocol Buffer

 

반면 Protocol Buffer의 경우, 바이너리 인코딩을 지원하기 때문에, 더 적은 용량으로 메세지를 전송할 수 있다. JSON으로는 82 바이트로 정보 표현을 했었는데, Protocol Buffer를 사용하면 33바이트만 사용한다.

 

사람이 이해하기는 불가능하지만, 전송 측면에서의 이점을 누리는 것이다. 또한, 이렇게 인코딩 및 디코딩 과정을 매번 거치게 되면 프로세싱 비용이 늘어나게 되는데, 네트워크 대역폭을 줄이고 CPU 소모량을 늘렸다고 이해해도 좋을 듯 하다.

 

아래는 지금까지 설명한 Protocol Buffer의 Worflow를 한 눈에 표현한 것이다.

 

PB Workflow

 

 

gRPC

gRPC의 핵심 개념은 크게 어렵지 않다. gRPC는 Stub이라는 개념을 통해 원격지의 주소 호출을 가능하게 하고, 메세지를 직렬화 및 역직렬화를 한다. 

 

그리고 이러한 stub은 Protocol Buffer 컴파일러로 '.proto' 파일을 grpc 플래그와 함께 컴파일하면 자동으로 생성된다. Stub은 어떠한 대리 기능을 수행해주는 주체라고 생각하면 된다. Stub을 사용하는 이유는 RPC의 개념을 생각해보면 떠올릴 수 있는데, 원격지의 주소 공간이 다르기 때문에, 프로시저나 함수를 호출하기 위해선 '주소공간과 관련 없이 호출해줄 수 있는 대리인'이 필요한 것이다. 

 

 

그리고 컴파일러로 생성된 Stub을 통해 서버에서, 그리고 클라이언트에서는 이를 Import하여 쉽게 소스 코드를 작성할 수 있게 된다. 즉, Python, Java 등으로 작성된 프로덕션 코드가 Stub에 프로시저 호출 요청을 보내면, Stub이 이를 적절히 바이너리 형태로 직렬화하여 전송하게 된다.

 

이 과정에서 응답이나 요청 형식을 스트림으로 지정할 수도 있고, 동기나 비동기 방식을 모두 사용할 수 있다. 혹은 데드라인을 지정할 수도 있는데, 이러한 부분들은 구체적인 사용과 관련된 측면이므로 이번 글에서는 다루지 않는다. 

 

아래는 gRPC의 동작 과정을 간단히 요약한 그림이다.

https://medium.com/@lchang1994/deep-dive-grpc-protobuf-http-2-0-74e6295f1d38

 

gRPC의 특징

이러한 gRPC는 아래와 같은 몇가지 특징을 가진다.

 

1. HTTP/2 사용

HTTP/1.1이 아닌 헤더 압축, 멀티플렉싱 등의 기능이 추가된 HTTP/2를 기반으로 동작한다. 따라서 HTTP/2가 가지는 성능 측면에서의 이점도 활용할 수 있다. HTTP/2에 대한 자세한 내용은 추후 따로 정리할 예정이다.

 

2. 특정 분야에서는 REST를 대체함

바이너리 형태로 전송하고, 또한 엄격한 Rule을 가지고 있기 때문에 JSON + HTTP/1.1 기반으로 동작하던 기존의 Server to Server 연결을 대체하게 되었다.

 

2. 브라우저 지원이 빈약함

주로 마이크로서비스에서 활용되는 등 Server to Server를 기반으로 설계되었기 때문에, 브라우저에서 gRPC를 사용할 수 있는 방법은 없다. 다만 JavaScript 라이브러리 등을 사용하면 우회적으로 사용할 순 있다.

 

3. Human Readable하지 않음

gRPC 메세지들은 Protocol Buffer에 의해 인코딩되기 때문에, 사람이 읽을 수 없다. 따라서 gRPC Payload를 중간 노드 등에서 해석하기 위해선 추가적인 처리가 필요하다.

 

참고 자료

https://en.wikipedia.org/wiki/Remote_procedure_call

https://web.eecs.umich.edu/~mosharaf/Readings/RPC.pdf

https://martin.kleppmann.com/2012/12/05/schema-evolution-in-avro-protocol-buffers-thrift.html

https://protobuf.dev/overview/

https://grpc.io/docs/what-is-grpc/core-concepts/

https://medium.com/naver-cloud-platform/nbp-%EA%B8%B0%EC%88%A0-%EA%B2%BD%ED%97%98-%EC%8B%9C%EB%8C%80%EC%9D%98-%ED%9D%90%EB%A6%84-grpc-%EA%B9%8A%EA%B2%8C-%ED%8C%8C%EA%B3%A0%EB%93%A4%EA%B8%B0-1-39e97cb3460