gRPC-Gateway - Golang learning step 10-1
- 公開日
- カテゴリ:ToolForMicroservices
- タグ:Golang,roadmap.sh,学習メモ

roadmap.sh > Go > Tool for Microservices > gRPC-Gateway の学習を進めていきます。
※ 学習メモとしての記録ですが、後にこのセクションを学ぶ道しるべとなるよう、ですます調で記載しています。
contents
- 開発環境
- 参考 URL
- gRPC-Gateway
- gRPC-Gateway のインストールと準備
- gRPC サーバーの実装
- gRPC-Gateway の実装
- gRPC & REST API の動作確認
開発環境
- チップ: Apple M2 Pro
- OS: macOS Sonoma
- go version: go1.23.2 darwin/arm64
参考 URL
gRPC-Gateway
gRPC-Gateway は、gRPC サーバーの API を RESTful API として公開するためのツールです。 Go 言語で実装された gRPC サーバーを HTTP/JSON クライアントでも利用できるようにします。 これにより、gRPC クライアントが使えない環境でも gRPC サーバーと通信できる というメリットがあります。
gRPC-Gateway は以下の仕組みで動作します:
.proto
ファイルに gRPC の定義を記述- gRPC サーバーを実装(
grpc-go
を使用) protoc
(Protocol Buffers コンパイラ)を使って gRPC-Gateway のコードを自動生成- 生成したコードを用いて、REST API を提供する HTTP サーバーを起動
gRPC-Gateway のインストールと準備
# プロジェクトを初期化
go mod init sample
1. 必要なツールをインストール
まず、必要なツールやライブラリをインストールします。
tools.go
を作成し、go mod tidy
を実行します。
//go:build tools
// +build tools
package tools
import (
_ "github.com/grpc-ecosystem/grpc-gateway/v2/protoc-gen-grpc-gateway"
_ "github.com/grpc-ecosystem/grpc-gateway/v2/protoc-gen-openapiv2"
_ "google.golang.org/grpc/cmd/protoc-gen-go-grpc"
_ "google.golang.org/protobuf/cmd/protoc-gen-go"
)
2. annotations.proto
gRPC-Gateway では google/api/annotations.proto
などの Google API 定義を利用するため、googleapis リポジトリをプロジェクトルートにダウンロードしておきます。
git clone https://github.com/googleapis/googleapis.git
2. .proto ファイルの作成
次に、.proto
ファイルを作成します。proto/hello.proto
を作成します。
syntax = "proto3";
package hello;
option go_package = "grpc/hello";
import "google/api/annotations.proto";
service HelloService {
rpc SayHello (HelloRequest) returns (HelloResponse) {
option (google.api.http) = {
post: "/v1/hello"
body: "*"
};
}
}
message HelloRequest {
string name = 1;
}
message HelloResponse {
string message = 1;
}
この .proto
ファイルには SayHello という API を定義しています。
name を送信すると message が返る、シンプルな API です。
3. protoc でコード生成
以下のコマンドで gRPC と gRPC-Gateway のコードを自動生成します。
protoc -I . -I googleapis --go_out=. --go-grpc_out=. --grpc-gateway_out=. \
--go_opt=paths=source_relative \
--go-grpc_opt=paths=source_relative \
--grpc-gateway_opt=paths=source_relative \
proto/hello.proto
.proto
ファイルを protoc
でコンパイルするときに、import
された .proto
ファイル(googleapis)を適切に解決するために -I
オプション(--proto_path
)を指定しています。
このコマンドを実行すると、以下の Go ファイルが生成されます:
proto/hello.pb.go
(gRPC メッセージ定義)proto/hello_grpc.pb.go
(gRPC サーバーのインターフェース)proto/hello.pb.gw.go
(gRPC-Gateway の変換コード)
gRPC サーバーの実装
次に、gRPC サーバーを作成します。
server/main.go
を作成し、以下を実装します。
package main
import (
"context"
"fmt"
"log"
"net"
"google.golang.org/grpc"
"google.golang.org/grpc/reflection"
pb "sample/proto"
)
// HelloService の実装
type helloServer struct {
pb.UnimplementedHelloServiceServer
}
func (s *helloServer) SayHello(ctx context.Context, req *pb.HelloRequest) (*pb.HelloResponse, error) {
return &pb.HelloResponse{
Message: fmt.Sprintf("Hello, %s!", req.Name),
}, nil
}
func main() {
// gRPC サーバーを起動
listener, err := net.Listen("tcp", ":50051")
if err != nil {
log.Fatalf("Failed to listen: %v", err)
}
grpcServer := grpc.NewServer()
reflection.Register(grpcServer)
pb.RegisterHelloServiceServer(grpcServer, &helloServer{})
log.Println("gRPC server listening on port 50051...")
if err := grpcServer.Serve(listener); err != nil {
log.Fatalf("Failed to serve: %v", err)
}
}
このコードでは SayHello メソッドを実装し、リクエストに対して "Hello, <name>!"
を返すシンプルな gRPC サーバーを作成しています。
gRPC-Gateway の実装
次に、gRPC サーバーを REST API として公開する gRPC-Gateway を実装します。
gateway/main.go
を作成し、以下を実装します。
package main
import (
"context"
"log"
"net/http"
"github.com/grpc-ecosystem/grpc-gateway/v2/runtime"
"google.golang.org/grpc"
pb "sample/proto"
)
func main() {
mux := runtime.NewServeMux()
opts := []grpc.DialOption{grpc.WithInsecure()}
// gRPC-Gateway を gRPC サーバーに接続
err := pb.RegisterHelloServiceHandlerFromEndpoint(context.Background(), mux, "localhost:50051", opts)
if err != nil {
log.Fatalf("Failed to start gRPC-Gateway: %v", err)
}
log.Println("gRPC-Gateway listening on port 8080...")
http.ListenAndServe(":8080", mux)
}
このコードでは RegisterHelloServiceHandlerFromEndpoint
を使い、gRPC サーバーの localhost:50051
に接続し、REST API として公開しています。
gRPC & REST API の動作確認
1. サーバーを起動
gRPC サーバーを起動します。
go run server/main.go
gRPC-Gateway を起動します。
go run gateway/main.go
2. 動作確認
01. gRPC プロトコルでのリクエスト
grpcurl(要インストール)を使って、サーバーに直接 gRPC プロトコルでリクエストを送信します。
grpcurl -plaintext -d '{"name": "Alice"}' localhost:50051 hello.HelloService/SayHello
結果は以下になります。gRPC のバイナリ通信が適切に処理されていることが確認できました。
{
"message": "Hello, Alice!"
}
02. HTTP (RESTful API) でのリクエスト
gRPC-Gateway 経由の REST API の動作確認です。curl
コマンドを使ってリクエストを送信します。
curl -X POST http://localhost:8080/v1/hello \
-H "Content-Type: application/json" \
-d '{"name": "Alice"}'
以下、HTTP/JSON で gRPC-Gateway 経由のリクエストが成功し、REST API として正しく動作していることを確認できました。
{
"message": "Hello, Alice!"
}
まとめ
gRPC-Gateway は、Go で実装した gRPC サーバーを、RESTful API(HTTP/JSON)として公開するためのツール。gRPC サーバーにアクセスできない環境(ブラウザや REST API クライアント)からでも、gRPC サーバーを利用できるようにする。
どんな時に役立つか
- Web フロントエンドやモバイルアプリから gRPC サーバーを利用したい時
- gRPC はバイナリ通信のため、通常のブラウザやモバイルから直接利用できないが、gRPC-Gateway を使えば REST API として利用可能になる。
- gRPC をサポートしていない外部サービスと連携する時
- REST API しか使えない外部システムとも gRPC-Gateway を介して連携できる。
- gRPC サーバーを構築しつつ、従来の REST API も提供したい時
- クライアントの移行をスムーズにしながら、新旧の API を共存させられる。
gRPC-Gateway のメリット
- gRPC の高性能な通信(バイナリ)と REST API の互換性を両立できる
- gRPC サーバーのコードをそのまま活用しつつ、追加で REST API を提供できる
- API の定義を
.proto
に統一することで、gRPC と REST API の一貫性を保てる - フロントエンドとバックエンドの技術スタックを柔軟に選べる
gRPC-Gateway のデメリット
- gRPC-Gateway を経由すると、直接 gRPC を使うよりレイテンシが増加する
- REST API のエラーハンドリングを適切に設定する必要がある
[Prev] Step 10-2: Rpcx
[Prev] Step 9-1: Testing