gRPC

 gRPC

2022.08.04

gRPCについて

protobufとgRPC

gRPCはRemote Procedure Call(ネットワーク越しのメソッド呼び出し)を実現するためにGoogleが開発したプロトコルです。protobufはデータフォーマットの1種です。
よく使われる通信プロトコルとしては他に

  • REST API + JSON
  • GraphQL

があります。REST APIとgRPCが, protobufとJSONが対応していると考えると分かりやすいです。 gRPC + protobuf を採用するメリットはprotobufが軽量で速い, gRPCがHTTP/2で速い, よって合わせるととても速い...ここに尽きると思います。


実装の流れ

  1. protoファイルにprotocol bufferでserviceを定義
  2. protocコマンドでコードを生成
  3. 自動生成されたコードをインターフェースとして実装


サンプルと解説

protoファイルを定義

/proto/service.protoに以下のファイルを定義。

Protobuf
syntax = "proto3";

option go_package = "<module_path>"; // これがないとコードが生成されない

package <package name>;

service SampleService {
  rpc CreateSample(CreateSampleRequest) returns (SampleResponse);
}

message CreateSampleRequest {
  string key1 = 1;
  string key2 = 2;
}

message SampleResponse {
  string status = 1;
  string description = 2;
}

コード生成

/protoディレクトリ内で

undefined
$ protoc --go_out=. --go_opt=paths=source_relative \
--go-grpc_out=. --go-grpc_opt=paths=source_relative \
service.proto

を実行すると/proto内にservice.pb.goとservice_grpc.pb.goの2つのファイルが生成されます。
service.pb.goはクライアント側, service_grpc.pb.goにはサーバー側で使うgoのコードが生成されており、これを使って実装していきます。


grpcurl

grpcはHTTP/2で速いと先ほど書きました。そのためせっかくサーバー側を実装してもHTTP/1.1までしか対応していないcurlでは呼び出すことができません。そこでcurlのようにgrpcを呼び出せるgrpcurlを紹介します。

undefined
$ grpcurl -plaintext -d '{"key1": "value1", "key2":"value2"}' localhost:<port> <package name>.SampleService/CreateSample

のように呼び出せます。


streaming RPC

gRPCがHTTP/2で実装されていると何回も繰り返し述べてきましたがHTTP/2がHTTP/1.1の大きな違いをご存知でしょうか? それは「ストリーム」という概念でこれによって1つのTCPコネクション内で並行して複数のリクエスト/レスポンスが処理できるようになりました。当然gRPCでも明示的にstreamを使うことができます。


Server-side streaming RPC

クライアントからのrequestがstreamか、サーバーからのresponseがstreamかでRPCは4つに大別できます。詳細はgRPC Basic Tutotialに譲りますがこのTutorialからServer-side streaming RPCを紹介します。 Server-side streaming RPCはクライアントからのリクエストを元にサーバーがstreamを返すRPCのことです。以下がサーバーの実装例です。

go
func (s *routeGuideServer) ListFeatures(rect *pb.Rectangle, stream pb.RouteGuide_ListFeaturesServer) error {
  for _, feature := range s.savedFeatures {
    if inRange(feature.Location, rect) {
      if err := stream.Send(feature); err != nil {
        return err
      }
    }
  }
  return nil
}

このようにサーバー側ではクライアントのリクエストの他にstreamを受け取り、stream.Send()を呼び出すごとにレスポンスを返しています。最後に手続きが終わったと知らせる目的でnilをreturnする必要があります。



まとめ

gRPCについて一通り紹介しました。gRPCには一通りミドルウェアも揃っているようなのでこのあたりも試そうと思います。



参考サイト

protobufスキーマとgRPC通信 gRPC Basic Tutotial github.com/fullstorydev/grpcurl 普及が進む「HTTP/2」の仕組みとメリットとは