初心者でもわかる!gRPCとは?

こんにちは、やまなかです。

今回はサーバー上の関数を実行するためのプロトコル(通信規格)であるRPC (Remote Procedure Call) を実現するため、Googleが開発したgRPCについてまとめていきます。

はじめにRPCとは?

以下のコードのように、ローカル上でmain関数からhello関数を呼び出すように、リモートサーバ上の関数を呼び出して応答を受け取ることを目的としたAPIアーキテクチャです。サーバ側やクライアント側が異なるフォーマットを使っていても、ルールを定義することでそれぞれに合わせたフォーマットで結果を受け取ることができる点がポイントです。メソッドを呼び出すときは、関数を呼び出すときのように引数をクエリ文字列に含めて送信します。

#include <stdio.h>

void hello(void){
    printf("Hello World");
}

int main(void){
    hello();

    return 0;
}

gRPC APIについて

クライアントとサーバ間のデータのやり取りは時代に応じて変化しており、従来のRPCやREST APIではHTTP/HTTPSベースでJSONデータのやり取りを行っています。REST APIについてはこちらの記事で同期が分かりやすくまとめてくれています。HTTP/HTTPSのデメリットとして、テキストベースで情報をやり取りするためデータの転送効率が悪い、バイナリデータを扱いにくいといった点が挙げられます。
gRPCはそれらのデメリットを解決しつつ、RPCの要件を満たすためにHTTP/2のPOSTリクエストとそのレスポンスを使ってこれを実現しています。呼び出す関数はリクエストのパスに含め、引数や戻り値はHTTPレスポンスボディに含めます。

gRPCの特徴

HTTP/2プロトコルの使用

HTTP/2は双方向のストリーミング通信をサポートしており、長時間の双方向、多重通信が可能です。もちろんREST APIのHTTP/1.1のようなリクエスト・レスポンス型のやり取りも可能です。

JSONの代わりにProtobufを使用

テキストベースであるJSONの代わりにProtobufというフォーマットを用いて転送効率の高速化やバイナリデータのやり取りを可能にしています。圧縮されたフォーマットであるため、JSONのように人間が読みやすい形にはなっていません。

多言語に対応

幅広い開発言語のサポートがあり、さまざまな言語で書かれたクライアントでも図のようにStubだけが必要で、異なる言語でも呼び出しが可能です。

Concept Diagram

Image Source : https://grpc.io/docs/what-is-grpc/introduction/

gRPCとREST APIの使い分け

REST

REST APIは、さまざまな形式で書かれたメッセージを受信・返信できますが、テキストベースの人間が読めるフォーマットで柔軟性があり、効率的で言語やプラットフォームにとらわれないJSONという形式が最も一般的に使用されます。gRPCより実装も簡単です。
外部ユーザーが理解しやすい公開APIであったり、シンプルなデータ通信を行いたい場合に使用します。

gRPC

リアルタイム通信が必要な場合、gRPCの双方向ストリーミング管理機能により、システムは単項のクライアント・レスポンス通信を待つことなく、リアルタイムでメッセージの送受信が可能です。
高速な通信やリアルタイムのストリーミングと大量のデータロードを必要とする内部システムに適しています。

デモ

goでquick startを動かしてみます。

動作環境

OS : WSL2 – Ubuntu 22.04.2 LTS
go:go1.18.1 linux/amd64
Protocol bufferコンパイラ:libprotoc 3.12.4

quick start

goをダウンロードします

$ rm -rf /usr/local/go && tar -C /usr/local -xzf go1.21.3.linux-amd64.tar.gz
$ export PATH=$PATH:/usr/local/go/bin
$ go version

バージョンが出てきたら完了
Protocol Bufferのコンパイラをダウンロードします

$ sudo apt install -y protobuf-compiler
$ protoc --version

3以上のバージョンなら完了です
プロトコルコンパイラ用のプラグインをインストールします

$ go install google.golang.org/protobuf/cmd/protoc-gen-go@v1.28
$ go install google.golang.org/grpc/cmd/protoc-gen-go-grpc@v1.2

パスを通します。

$ export PATH="$PATH:$(go env GOPATH)/bin"

サンプルコードを入手します。

$ git clone -b v1.58.2 --depth 1 https://github.com/grpc/grpc-go

Quick startのサンプルディレクトリに移動します。

$ cd grpc-go/examples/helloworld

サーバを実行します。

$ go run greeter_server/main.go
2023/11/21 15:09:33 server listening at [::]:50051

別のターミナルを開いてクライアントを起動します。

$ go run greeter_client/main.go
2023/11/21 15:09:46 Greeting: Hello world

サーバ側でクライアント側の入力を確認します。

2023/11/21 15:09:46 Received: world

gRPCのサービスを追加してみます。
.protoファイルでサービスを定義しているので、
grpc-go/examples/helloworld/helloworld/helloworld.protoを編集します。

// The greeting service definition.
service Greeter {
  // Sends a greeting
  rpc SayHello (HelloRequest) returns (HelloReply) {}
  // Sends another greeting
  rpc SayHelloAgain (HelloRequest) returns (HelloReply) {} //<--追加したところ
}

//The request message containing the user's name.
message HelloRequest {
  string name = 1;
}

// The response message containing the greetings
message HelloReply {
  string message = 1;
}

保存して.protoファイルを再コンパイルします。

$ protoc --go_out=. --go_opt=paths=source_relative \
--go-grpc_out=. --go-grpc_opt=paths=source_relative \
helloworld/helloworld.proto

新しいメソッドを追加する必要があるのでサーバを更新します。
grpc-go/examples/helloworld/greeter_server/main.goに次の関数を追加します。

func (s *server) SayHelloAgain(ctx context.Context, in *pb.HelloRequest) (*pb.HelloReply, error) {
    return &pb.HelloReply{Message: "Hello again " + in.GetName()}, nil
}

クライアント側で先ほど追加した関数を実行してみます。
grpc-go/examples/helloworld/greeter_client/main.goのmain()末尾に次のコードを追加します。

r, err = c.SayHelloAgain(ctx, &pb.HelloRequest{Name: *name})
if err != nil {
    log.Fatalf("could not greet: %v", err)
}
log.Printf("Greeting: %s", r.GetMessage())

忘れずに保存して実行します。

$ go run greeter_server/main.go
2023/11/21 15:19:09 server listening at [::]:50051

$ go run greeter_client/main.go --name=yamanaka
2023/11/21 15:19:48 Greeting: Hello yamanaka
2023/11/21 15:19:48 Greeting: Hello again yamanaka

サーバ側の応答
2023/11/21 15:19:48 Received: yamanaka

無事追加した関数を実行することができました。

まとめ

gRPCはRPCを実現するためにGoogleが開発したプロトコルでした。マイクロサービスなど、インタラクションが必要なシステム間のデータ伝送を高速化するために使われます。一概にREST APIとgRPCのどちらが優れているとは言えません。求められる要件によって適切なプロトコルは異なるので、よく考えて採用しましょう。

参考文献

gRPC Motivation and Design Principles | gRPC

サービス間通信のための新技術「gRPC」入門 | さくらのナレッジ

gRPC と REST の比較 – アプリケーション設計の違い – AWS

gRPCって何? #WebAPI – Qiita

ご覧いただきありがとうございます! この投稿はお役に立ちましたか?

役に立った 役に立たなかった

0人がこの投稿は役に立ったと言っています。

コメントを残す

メールアドレスが公開されることはありません。 が付いている欄は必須項目です