gRPC란 무엇이고 어떻게 구성되나요?

Nov 23, 2018


gRPC

  • 다른 개발자 분들께 공유할 용도로 개략적으로 쓰인 글입니다. 번역이 잘못되었을 수도 있습니다 ㅠ

    gRPC란 무엇인가요?

    gRPC faq

  • gRPC는 어떤 환경에서도 동작하는 모던한 오픈소스 원격 프로시저 요청 (Remote Procedure Call, RPC) 프레임워크입니다.
  • gRPC는 구글에서 10년 이상동안 수 많은 MSA와 데이터센터 사이를 연결하기 위해 사용하던 Stubby라고 부르던 범용 RPC 인프라를 크로스플랫폼, 오픈소스화해서 만들어졌습니다.
    • Stubby를 공개하기엔 구글 사내 서비스와 너무 타이트하게 연결이 있어서 spdy, http/2, QUIC 등을 지원하는 기능을 추가하고 Stubby기능을 좀 더 표준화하도록 수정해서 오픈되었습니다.
  • gRPC stub
    • client side에서 요청을 grpc 형태로 만들어주는 역할을 하는 컴포넌트의 이름입니다.

gRPC가 반드시 제공해야할 기능

기타 등등 … gRPC Principles 참조

gRPC의 장점

  • Low Latency, Highly scalable, distributed systems.
    • 클라우드 서버와 커뮤니케이션하는 모바일 클라이언트를 지원하는데에 초점이 맞춰져있습니다.
  • http/2를 이용한 reverse proxy 가능
    • 서버간에 http 1.1 keep-alive로 통신하고 있을 경우, 특정 요청이 holding되면 다음 요청도 전부 holding되는 문제가 생길 수 있습니다
    • http/2 reverse proxy를 통해 multiplex하게 서버와 요청을 주고 받을 수 있게 됩니다.
  • vendasta에서 gRPC 사용을 통해 얻은 세 가지 이점
    • gRPC를 이용해 5개 이상의 언어로 이루어진 SDK-서버 간의 통신을 통합할 수 있었습니다.
      • SDK 개발시에 더 이상 개발자가 api문서를 작성하지 않아도되고, api 형태가 어떤식으로 되어있는지 물어볼 필요가 없어졌습니다
      • vendasta에서는 하나의 서버에 통신하기 위해 여러 언어로 SDK를 만들어서 해당 이점이 극대화될 수 있었습니다.
    • gRPC 사용을 통해, 어떤 요청이 끝날 때 까지 기다릴 필요가 없이, 첫 요청이 들어오면 순서와 관계없이 서버에서 응답을 내보내주면 되기 때문에 첫 화면 구성이 더 빨라졌습니다.
    • JSON을 프로토콜 버퍼로 변환하고난 뒤에, 서버/클라이언트에서 Serialization / Deserialization하는 것에 대한 어려움을 해소할 수 있게 되었습니다.
      • gRPC에서는 하나의 요청 / 응답 형태에서 타입이 정해진 message를 사용합니다.
  • Protocol Buffer
    • XML, json등으로 들어온 요청 / 응답을 Protocol Buffer를 이용해 직렬화하여 더 작은 크기의 요청 / 응답으로 만들 수 있습니다.
    • 기본적으로는 base128을 이용한 byte array 직렬화를 사용하지만, 사용방식에 따라 json, text 직렬화로 사용 가능합니다.
  • 자바에서의 protoBuf의 예시입니다.
    • 전화번호부를 만들기 위한 메시지 형태를 구성합니다.
syntax = "proto2";

package tutorial;

option java_package = "com.example.tutorial";
option java_outer_classname = "AddressBookProtos"; 
// message를 갖고 있는 outer class이름을 지정합니다.
// model은 message로부터 생성합니다. 이를 위해 protoc 라는 compiler를 사용해야합니다.

message Person {
  required string name = 1;
  required int32 id = 2;
  optional string email = 3;

  enum PhoneType {
    MOBILE = 0;
    HOME = 1;
    WORK = 2;
  }

  message PhoneNumber {
    required string number = 1; // required를 쓸 때는 주의해야합니다. 누락될 경우 RuntimeException이 나기 때문에, 가능하면 optional과 repeated로 메시지를 구성하는 편이 좋습니다.
    optional PhoneType type = 2 [default = HOME];
  }

  repeated PhoneNumber phones = 4;
}

message AddressBook {
  repeated Person people = 1;
}
  • protoc의 사용법 참고
  • protoc로 만들어진 클래스를 이용해, 자바에서는 아래와 같이 메시지를 생성할 수 있습니다
Person john =
  Person.newBuilder()
    .setId(1234)
    .setName("John Doe")
    .setEmail("jdoe@example.com")
    .addPhones(
      Person.PhoneNumber.newBuilder()
        .setNumber("555-4321")
        .setType(Person.PhoneType.HOME))
    .build();
  • builder가 쓰기에 별로라면, 직접 커스텀코드를 작성해서 만들 수 있다고 되어있긴 합니다.

gRPC life cycle

  • RPC 요청 1
    • 처음 gRPC요청을 하기 위해 metadata를 서버에 전송하고, 서버에서는 deadline을 설정해서 client에 돌려줍니다.
      • deadline은 RPC 요청의 성공 여부와 관계 없이 해당 RPC 요청을 언제 timeout시켜서 종료할지에 대한 정보를 갖고 있습니다.
    • 서버는 바로 서버의 metadata를 response하거나, client의 다음 요청을 기다립니다
      • 서버의 metadata는 반드시 response보다 먼저 보내야 합니다.
    • 서버가 클라이언트로부터 요청을 받았다면, 어떤 것이든지 응답을 보내줘야 합니다.
    • status가 ok라면 client가 응답을 받아서 요청을 완료합니다.
  • Server streaming RPC
    • 서버에서는 클라이언트에 response를 보내고 뒤따르는 메타데이터가 있다면 보낸 뒤에 complete를 의미하는 패킷을 보내는 것으로 연결을 종료합니다.
  • Client streaming RPC
    • 클라이언트에서는 request를 하나로 보내는 대신 여러 개를 스트리밍으로 보낼 수 있습니다.
    • 서버는 일반적으로 하나의 response를 보내지만, 클라이언트가 모든 request를 보낼 때까지 기다릴 필요는 없습니다.
  • Bidirectional streaming RPC
    • 양방향 스트리밍 RPC에서는 클라이언트의 요청으로 stream이 연결된 뒤에, 서버에서는 클라이언트의 추가 요청을 기다립니다.
    • 어플리케이션에 따라 다르겠지만, 클라이언트와 서버는 순서와 관계없이 읽기/쓰기를 반복할 것입니다.
      • 스트림은 완전하게 독립적으로 동작합니다.
    • 서버는 클라이언트의 모든 요청을 기다리거나, 핑퐁식으로 메시지를 주고받거나 할 수 있습니다.
  • 데드라인과 타임아웃
    • gRPC는 클라이언트가 DEADLINE_EXCEEDED 에러가 발생해서 RPC가 종료되기 전에 얼마나 RPC를 기다려야 하는지에 대해 정할 수 있습니다.
    • 서버에서는 쿼리를 날려서 특정 RPC가 time out이 되었는지, RPC 커넥션 시간이 얼마나 남았는지를 확인할 수 있습니다.
    • 언어별로 데드라인이나 타임아웃의 형태는 다르게 설정됩니다
      • 어떤 언어에서는 디폴트 데드라인이 없기도 하고, 어떤 언어는 특정 시각이 지나면 데드라인으로 판단하기도하고, 몇몇 언어는 특정 시간이 지나면 타임아웃으로 판단하기도 합니다.
  • RPC Termination
    • gRPC에서는, RPC의 종료를 클라이언트 / 서버에서 각각 판단합니다.
      • 서버에서는 response가 finish 됐을 때, 클라이언트에서는 deadline이 지났을 때 등.
  • Canceling RPCs
    • 서버, 클라이언트 모두 어떤 시점에서든지 RPC 연결을 취소할 수 있습니다.
    • RPC를 취소한다는 것이 요청을 “롤백 (undo)” 한다는 의미는 아닙니다.
  • Metadata
    • metadata는 key-value list로 정의된 RPC 요청에 대한 정보입니다.
      • header와 비슷한 형태인듯합니다.
  • Channels
    • 채널은 client 에서 stub을 만들 때 생성되는 호스트와 포트로 특정되는 gRPC 서버 연결에 대한 정보를 가집니다.
    • 채널의 설정을 변경하거나, state (connected, idle)를 확인할 수 있습니다.

gRPC 적용전 환경세팅

  • centOS 6에서 C++ gRPC가 빌드되지 않는 문제가 있었습니다 (2017년 1월 18일)
    • https://github.com/grpc/grpc/issues/9365
    • gRPC 자바를 사용하면 되긴 합니다(https://grpc.io/docs/quickstart/java.html)
      • JDK 7 이상
  • 아직 웹에서는 안정적으로 사용할 수 없습니다.
    • 다만 웹용 프로젝트가 2018년 10월 23일에 릴리즈되긴 했습니다(https://github.com/grpc/grpc-web, https://grpc.io/blog/grpc-web-ga)
      • grpc-web-arch
      • gRPC-Web은 브라우저-서버간의 gRPC통신을 가능하게 만들어줍니다.
  • NGINX의 버전이 최소 1.13.10(18년 3월 20일 릴리즈, 18년 4월 17일 1.14.0에 포함되었습니다)이 되어야 합니다.
    • Server Push가 적용되어 있습니다
    • gRPC proxy pass가 적용되어있습니다.
  • NGINX에 http/2 reverse proxy가 도입되지 않을 예정이기 때문에, http/2 또는 h2c로 nginx 서버간에 통신을 하기위해서는 gRPC를 사용하거나 server push를 사용해야 합니다.
    • 현재 NGINX 에서는 Nginx Plus 라는 걸 만들어서 장사를 하려고 하기 떄문에, 당분간 http/2 proxy가 만들어지진 않을 것 같습니다.
    • 무려 1개 인스턴스당 최소 연간 2500달러입니다. 엔터프라이즈 급은 연간 5000달러입니다 참고

서비스에 gRPC 적용 방식 예정

  • Mobile, PC
    • Client(web)-REST => Proxy Server (Nginx -> gRPC Gateway) => Server (Nginx -> gRPC stub -> spring)

Q&A

  • protoc로 나온 모델 파일 (java의 경우 class)에서 message 파일 (.proto)을 만들어낼 수 는 없나요?
    • 현재는 불가능합니다.
  • client - server(load balancer) - server(backend) 사이의 stream은 하나를 공유해서 사용하게 되나요?
    • client - server(backend) 사이의 stream이 독립적이기 때문에, server (load balancer) - server(backend) 사이의 stream도 여러 개가 생성되게 됩니다.
  • thrift와 비교해서 장점은 어떤 것이 있나요?
  • 당장 사용할 수 있나요?
    • api g/w 에서 보안 부분을 처리해줘야합니다 (gRPC 요청에 대한 hmac 인증 적용 등)
    • gRPC에서 채널단위 보안 / 요청단위 보안에 관련된 API를 제공하지만, token방식의 보안은 구글 OAuth2를 사용할 때만 지원하는듯 합니다. abstact 클래스를 직접 구현해서 기능을 확장하도록 권장하고 있습니다.
    • gRPC authentication

기타 문서