🚄
gRPC触ってみた
gRPCの学習をしたので、振り返り用に共有しておきます。
ディレクトリ構成などは、以下のリポジトリを参考にしてください。gRPCとREST APIの違いについて
gRPCとREST APIは、システム間でデータを交換するための2つの異なる通信プロトコルです。以下に主な違いを簡潔に記載します。
- プロトコル
・ REST APIはHTTP/1.1プロトコルを使用して通信し、主にWebサービスのために設計されている。
・ gRPCはHTTP/2を基盤とし、低遅延、高スループットの通信を実現するために設計されている。 - データフォーマット
・ REST APIでは、主にJSONまたはXML形式のテキストデータを使用している。
・ gRPCでは、バイナリ形式のプロトコルバッファ(ProtoBuf)を使用するため、データのシリアライゼーションとデシリアライゼーションが高速である。 - API設計
・ REST APIはリソース指向であり、HTTPメソッド(GET、POST、PUT、DELETEなど)を使用してリソースの状態を操作します。
・ gRPCはサービス指向で、メソッド呼び出しをリモートプロシージャコール(RPC)として定義します。これにより、クライアントとサーバー間でのメソッド実行が直接的に行える。
事前準備
- Go用のプロトコル コンパイラ プラグインをインストールします。
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
-
PATH
を更新して、protocコンパイラーがプラグインを見つけられるようにする
export PATH="$PATH:$(go env GOPATH)/bin"
-
protoc
コマンドを使用するためにprotobuf
の依存をインストール
brew install protobuf
-
curl
のようにgRPCにリクエストするためにgrpcurl
をインストール
brew install grpcurl
Protoファイルの自動生成
hello.proto
という名前のprotoファイルを作成します。
スキーマ定義については、以下の記事を参考にしました。
// プロトコルバッファの構文バージョンを指定
syntax = "proto3";
// パッケージの定義
package hello;
// importの定義
// 他の.protoファイルで定義したメッセージ型を使いたい場合に使う
// import "google/protobuf/timestamp.proto";
// 生成されるGo言語のコードが属するパッケージを指定
option go_package = "/pb";
// gRPCサービス
service HelloService {
// サービス内のRPCメソッドを定義
rpc SayHello(HelloServiceRequest) returns (HelloServiceResponse) {}
}
message HelloServiceRequest {
string name = 1;
}
message HelloServiceResponse {
string message = 1;
}
protoファイルをgoファイルに変換します。
go_out
オプションはリクエスト/レスポンス部分のコードの出力先を、go-grpc_out
オプションはサービス部分のコード出力先を指定
protoc --go_out=. --go-grpc_out=require_unimplemented_servers=false:. ./proto/hello.proto
サーバー側実装
package main
import (
"context"
"fmt"
"log"
"net"
"github.com/Watson-Sei/test-grpc/pb"
"google.golang.org/grpc"
"google.golang.org/grpc/reflection"
)
type Server struct {
pb.HelloServiceServer
}
func (s *Server) SayHello(ctx context.Context, req *pb.HelloRequest) (*pb.HelloResponse, error) {
log.Print("Hello " + req.Name)
return &pb.HelloResponse{
Message: "Hello " + req.Name,
}, nil
}
func main() {
lis, err := net.Listen("tcp", fmt.Sprintf(":%d", 50051))
if err != nil {
panic(err)
}
server := grpc.NewServer()
pb.RegisterHelloServiceServer(server, &Server{})
server.Serve(lis)
}
クライアント側実装
package main
import (
"context"
"fmt"
"log"
"github.com/Watson-Sei/test-grpc/pb"
"google.golang.org/grpc"
)
func main() {
opts := grpc.WithInsecure()
cc, err := grpc.Dial("localhost:50051", opts)
if err != nil {
log.Fatal(err)
}
defer cc.Close()
client := pb.NewHelloServiceClient(cc)
request := &pb.HelloRequest{Name: "Watson Sei"}
res, err := client.SayHello(context.Background(), request)
if err != nil {
log.Fatal(err)
}
fmt.Printf("Receive response => %s ", res.Message)
}
gRPCurlでメソッドを呼び出す
サービス一覧を表示する
このコマンドの実行結果は、サーバーで利用可能なgRPCサービスの名前をリストアップします。この例では、Hello
という名前のサービスと、gRPCサーバーのリフレクション機能を提供する。
サーバーリフレクションについて
-
-plaintext
:セキュリティで保護されていない接続(TLSを使用しない接続)を示します。これは、開発やテスト環境で適している。 -
-import-path .
:プロトコルファイル(.proto
ファイル)が現在のディレクトリにあることを指定します。これにより、grpcurl
あh指定されたディレクトリから必要なプロトコル定義を読み込みます。 -
-proto hello.proto
:使用するプロトコルファイルの名前を指定します。 -
localhost:50051
:接続先のgRPCサーバーのアドレスとポートを指定 -
list
:サーバーで利用可能なgRPCサービスのリストを表示するコマンド
grpcurl -plaintext -impor
t-path . -proto proto/hello.proto localhost:50051 list
>> hello.HelloService <- サービスが出力される
その他のgrpcurl操作については、以下の記事を参考にしてください。
サーバー側には以下を追記してください。
import "google.golang.org/grpc/reflection"
func main() {
// サーバーリフレクション設定
reflection.Register(server)
}
まとめ
gRPCを実際に少し使ってみた感じ、学習にかなりの時間を要するため少し採用に戸惑ってしまいますが使いこなせれば急変するAPI仕様への厳格な開発が行えるのではないかと感じました。
とはいえ、プロトコルファイルの書き方は慣れないものではあります。
Discussion