gRPC

Intro

gRPCProtocol Buffer라는 것에 대해서 설명한다. gRPCProtocol Buffer를 교환 가능한 형태의 IDL(Interface Definition Language)과 message를 기술하기 위해서 사용한다.

gRPC

gRPC에서 client application은 전혀 다른 machine의 server application에 존재하는 method를 마치 local object로 존재하는 것처럼 직접적으로 호출할 수 있다. 이는 실제로 distributed application(ex. Web 개발에서의 BackEnd, FrontEnd | Micro Service Architecture)과 service들을 제작할 때 굉장히 유용하다. 다른 RPC System과 마찬가지로, gRPC는 원격으로 parameter를 제공하여 호출할 수 있는 method와 return type을 기술하여 service를 정의한다. Server 측에서는 interface를 구현하고, 실제로 client의 호출을 처리할 gRPC server를 실행시킨다. Client 측에서는, server의 method와 동일한 method를 제공하는 stub(gRPC client)를 가진다.

gRPC overview

gRPC client와 server는 다양한 환경(Cloud, Server, Desktop, etc...)에서도 실행이 가능하며, 서로 의사소통이 가능하고, gRPC를 지원하는 어떤 언어로도 구현이 가능하다. 즉, Server와 Client에서 어떠한 언어를 사용해도 상관이 없다. 또한, 최신 Google API들을 gRPC로도 제공하고 있기 때문에 쉽게 이용이 가능하다.

기본적으로, gRPC는 구조화된 데이터의 Serialization을 위한 Google의 Open Source인 Protocol Buffer를 사용한다.

Protocol Buffer의 데이터의 구조는 .proto 로 끝나는 일반적인 텍스트 파일(proto file)에 정의한다. Protocol Buffer data는 message라는 형태로 작성되며, 각 message는 여러 name-value 쌍으로 구성된 field들로 구성된 작은 정보 record이다. 아래는 message의 간단한 예제이다.

1message Person { 2 string name = 1; 3 int32 id = 2; 4 bool has_ponycopter = 3; 5}

일단 data 구조를 정의하면, 선호하는 language를 통해 Protocol Buffer Compiler(protoc)를 사용해서 data access class들을 생성할 수 있다. 이는 각 field에 대한 간단한 접근자(name(), set_name())와 전체 구조를 raw bytes로 serialization/parsing할 수 있는 method를 제공한다. 예를 들어 C++를 선택하였다면, protocPerson이라는 class를 생성할 것이고, 이를 이용해서 Person에 대한 message를 얻거나, serialization, 또는 parsing을 수행할 수 있다.

또한, gRPC service를 proto file에 paramter, return type와 같이 정의할 수 있다.

1// The greeter service definition 2service Greeter { 3 // Sends a greeting 4 rpc SayHello (HelloRequest) returns (HelloReply) {} 5} 6 7// The request message containing the user's name. 8message HelloRequest { 9 string name = 1; 10} 11 12// The response message containing the greetings 13message HelloReply { 14 string message = 1; 15}

gRPCprotoc와 특별한 gRPC plugin을 사용하여 proto file에서 code를 생성한다. 이를 통해, message type들을 얻고, serialization할 수 있는 일반적인 Protocol Buffer code와 gRPC client 와 server code를 얻을 수 있다.

Protocol Buffer

gRPC를 제대로 활용하기 위해서는 Protocol Buffer에 대해서 자세히 알아두어야 하기에 별도의 섹션으로 분리하여 설명한다. Protocol Buffer는 구조화된 데이터를 Serialization(구조화된 데이터를 byte형태로 변환하는 과정 또는 이를 통해서 생성된 결과물)하고, 다시 Parsing(Serialization을 통해서 생성된 데이터를 원래의 구조화된 데이터로 변환하는 과정)하는 과정을 언어 중립적(어떤 프로그래밍 언어에서도 사용가능한), 플랫폼 중립적(어떤 플랫폼에서도 사용가능한)으로 확장한 메카니즘이다. 이는 JSON과 유사하지만, 더 작고 더 빠르며, 실제 언어와의 연결을 자동으로 생성해준다.

Protocol Buffer는 다음과 같은 도구의 집합이다.

  1. Definition Language(.proto file을 작성하는 언어이다.)
  2. data의 interface, language에 따른 runtime library들, data를 위한 serialization format Proto Compiler에 의해 생성된 code

핵심 Idea

Protocol Buffer는 megabyte 정도 크기의 구조화된 data를 위한 Serialization format을 제공한다. 이는 단기간의 Network Traffic에서부터 장기간 data 저장에 모두 유리하다. Protocol Buffer는 기존 data를 무효화거나 update하는 추가적인 code 작업없이 새로운 정보로 확장되어질 수 있다. 뿐만 아니라 하위 호환성(이전 version의 data의 요청에도 대응하는 것)을 유지하는 것이 중요하기 때문에 이들을 장기간 유지한다. Protocol Buffer는 새로운 field의 추가/삭제에도 서비스의 중단 없는 환경을 제공한다.

Protocol Buffer들은 구글에서는 굉장히 일반적으로 사용되는 format이다. 이는 대게 광범위한 server간 통신에 사용될 뿐만 아니라 disk에 data를 기록하는데에도 사용되어진다. Protocol Buffer message와 service들은 .proto 파일에 기술자에 의해 정의되어진다.

Proto Compiler는 build time에 .proto file에 기반하여 이에 적힌 Protocol Buffer를 조작하기 위한 다양한 language의 코드를 생성하기 위해서 실행되어진다. 각각에 생성된 class들은 각 field에 접근하기 위한 간단한 접근자들을 포함하며, serialization과 parsing을 위한 method를 제공한다.

장점

Protocol Buffer는 구조화된 data를 어떤 언어에서든, 어떤 platform에서든 광범위한 상황에서 serialization/parsing할 필요가 있을 때 굉장히 유용하다. gRPC로 통신도 가능할 뿐만 아니라 이를 위한 data storage도 제공하기 때문이다. 또한 다음과 같은 장점을 포함한다.

  1. 적은 data 저장공간
  2. 빠른 parsing
  3. 많은 programming 언어와의 호환성
    • C++, C#, Java, Kotlin, Go, Dart, Python, Ruby, Javascript(Node), PHP
  4. 자동으로 생성된 class를 통한 최적화된 기능성

한계점

Protocol Buffer가 항상 모든 데이터에 대해서 적절한 것은 아니다.

  • Protocol Buffer는 전체 message들이 memory에 모두 load될 수 있고, object graph보다는 크기가 작다고 가정한다. 따라서, 1~2 megabyte 내외의 크기를 넘어선다면, 여러 개의 copy를 만들게 될 것이고, 이는 memory 사용의 비효율을 초래할 것이다.
  • Protocol Buffer가 serialization되었을 때, 동일한 data는 다양한 형태의 binary serialization 형태를 갖는다. 그렇기에 이들을 직접적으로 parsing하기 전까지는 이것이 서로 동일한지 알 수 있는 방법이 없다.
  • Message는 JPEG, PNG와 같은 전용 압축 알고리즘이 존재하지 않는다. (하지만, zip, gzip 등은 가능하다.)
  • Protocol Buffer message들은 float 및 다차원 배열을 많이 포함하는 과학 및 공학 용도에서 크기와 속도 면에서는 최대 효율보다 낮은 성능을 보인다. 이러한 요구가 많은 경우에는 overhead를 줄이기 위해서 FITS 또는 이와 유사한 방식을 활용해야한다.
  • Protocol Buffer Object Oriented Language가 아닌 경우(Fortran, IDL) 지원이 어렵다.
  • Protocol Buffer message들은 자체적으로 data를 정의할 수는 없고, 사용자에 의해서 정의된 schema에 기반한다. 따라서, data에 상응하는 .proto file이 없다면, 이를 완벽하게 해석하는 것은 불가능하다.
  • Protocol Buffer는 여러 organization의 표준은 아니다. 따라서, 일반적인 사용 환경에서는 적절하지 않을 수 있다.

작동 방식

  1. data 구조를 정의하기 위해서 .proto file을 생성한다.
  2. .proto file을 기반으로 protoc는 code를 생성한다.
  3. 생성된 code와 project code를 같이 compile한다.
  4. 이를 통해서 생성되는 Procotol Buffer class들을 활용하여 serialization, 공유 및 parsing이 가능하다.

Protocol Buffer class들은 다양한 method를 포함한다. 예를 들어, 특정 데이터를 stream, file로 부터 받아오거나 개별적으로 value를 추출하거나 해당 데이터가 존재하는지 확인하거나 다시 serialization하여 stream 또는 file로 만드는 과정이 가능하다.

Definition Syntax

.proto file을 정의할 때, 먼저 optionality를 설정한다. 이는 optional 또는 repeated 또는 singular 중에 하나를 선택할 수 있다. (v2에는 required도 존재하지만 이를 사용하는 것을 좋지 않은 pattern으로 여긴다.)

위의 사항을 정한 후에는 data type을 기술해야 한다. Protocol Buffer는 보편적으로 사용되어지는 data type(Integer, Boolean, Float)을 제공한다.

field는 아래와 같은 것도 포함한다.

  • message : data의 설정을 반복하는 경우 이를 사용할 수 있다.
  • enum : 해당 type의 종류를 value들의 집합으로 표현할 수 있다.
  • oneof : message가 여러 optional field를 포함할 때, 단 하나의 field만 가지도록 할 수 있다.
  • map : key-value 형태로 기술할 수 있다.

optionality와 field type을 설정한 후에, field number를 할당해야 한다. 이는 다른 목적으로 사용하거나 재사용되어서는 안된다. 특정 field를 제거했다면, 이 숫자를 재사용하지 않도록 반드시 유의해야 한다.

추가적인 Data Type

Protocol Buffer는 다양한 scalar value type을 제공한다. 또한, 자신만의 합성 data type을 message를 정의함으로써 생성할 수 있다. 또한 보편적으로 사용되어지는 다음과 같은 type들도 추가적으로 정의되어있다.

  • Duration : 시간을 의미한다. (ex. 42s)
  • Timestamp : 시각을 의미한다. (ex. 2017-01-15T01:30:15.01Z)
  • Interval : 시간 사이 거리를 의미한다. (ex. 2017-01-15T01:30:15.01Z - 2017-01-16T02:30:15.01Z)
  • Date : 달력에서의 전체 날짜를 의미한다. (ex. 2025-09-19)
  • DayOfWeek : 일주일에 요일을 의미한다. (ex. Monday)
  • TimeOfDay : 하루에서 시간을 의미한다. (ex. 10:42:23)
  • LatLng : 위도와 경도를 의미한다.
  • Money : 화폐 단위를 의미한다.
  • PostalAddress : 우편주소를 의미한다.
  • Color : RGBA color 값을 의미한다.
  • Month : 년에 따른 월을 의미한다. (ex. April)

Outro

현재까지 사용해본 RPC는 ReST API, GraphQL이 있었다. ReST는 워낙 유명하고, JSON, xml기반으로 많이 사용하기 때문에 누구나 알법하다. 그리고, graphQL은 더 유동적인 구현 방식을 제공했기 때문에 Gatsby와 같은 platform이나 Github에서도 사용할 정도로 유명해졌다. 앞 서 얘기한 두 개는 BackEnd에서 FrontEnd로 제공하는 시스템에서 주로 이용된다는 것을 이번 기회로 깨달은 것 같다. 그렇기에 좀 더 사람이 이해하기 쉬운 구조로 되어있을 뿐만 아니라 굉장히 친숙하다. 하지만, gRPC는 성능 향상에 좀 더 초점을 두었고, Server간 통신에 더 방점을 둔 것이라는 것이 와닿았다. 여러 Virtual Machine이 많아지는 가운데에 이들간에 빠른 통신과 각 Machine간의 호환을 위한 여러 기능들에 좀 더 초점을 맞추고 있다는 것을 알게 되었다. 아마 앞으로 gRPC를 사용한다면 이러한 용도로 가장 많이 사용할 것 같다. 아마 이래서 주요 사용처가 Network Vender(CISCO, Juniper), CNCF, Netflix 등인 것일 수도 있겠다. 끝으로 이를 이용한 구현을 직접해보아야 더 나은 설명이 가능할 것 같으므로 후에 이에 대해서 더 자세히 다루겠다.

Reference

Comments