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

【6/19開催】Kong Community Japan Meetup #4
本イベントでは、Kong Inc. のVP of ProductであるReza Shafii氏もプレゼンターとして参加。当社からはアーキテクト マネージャーの槌野の登壇が決定!参加無料です!!

【6/21開催】開発者目線でのSBOMとの向き合い方
SBOMの導入から開発者がSBOMの作成・管理を自動で行っていくための方法(デモ)を紹介します。

【7/5開催】azd+Terraform? ~ポイントを押えてAzure上へのアプリケーション環境をラクチン構築~
ツールの概要説明から、実際の開発におけるポイントをお伝えします!

【7/19開催】現場で役立つAzure神小技10+α 〜生成AI,RAG,コスト削減など旬な技術満載のLT大会〜
Azureの最新技術や実用的な小技を紹介する特別なライトニングトーク大会を開催します!

【7/26開催】最適なIaCツールを選ぼう
プロジェクトでのツール選びに困らないための重要な観点をご説明します!

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

今回はサーバー上の関数を実行するためのプロトコル(通信規格)である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

アバター画像
About やまなか 6 Articles
サイオステクノロジーに2023年新卒入社。高専では半導体細線の量子電子伝導、大学では聴覚の認知知覚の研究を行っており、現在はコンテナやインフラ周りを勉強中。この通り何でもやります。

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

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

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


ご覧いただきありがとうございます。
ブログの最新情報はSNSでも発信しております。
ぜひTwitterのフォロー&Facebookページにいいねをお願い致します!



>> 雑誌等の執筆依頼を受付しております。
   ご希望の方はお気軽にお問い合わせください!

Be the first to comment

Leave a Reply

Your email address will not be published.


*


質問はこちら 閉じる