RESTful API/gRPC API提供
gRPC-GatewayはgRPCで提供されているAPIをRESTful APIに変換して、提供するためのコード生成ができます。
なので、HTTP/2をサポートしていない環境からは、RESTful API経由で、リクエストを受け付けるといった使い方ができます。
*.protoファイルで一括管理できて、サーバーの管理がとても楽になります。
備忘録です。
環境
% go version
go version go1.22.5 darwin/arm64
% protoc --version
libprotoc 3.20.3
実行ファイルをインストール
go install \
github.com/grpc-ecosystem/grpc-gateway/v2/protoc-gen-grpc-gateway@latest \
github.com/grpc-ecosystem/grpc-gateway/v2/protoc-gen-openapiv2@latest
version
% protoc-gen-grpc-gateway --version
Version v2.22.0, commit unknown, built at unknown
% protoc-gen-openapiv2 --version
Version v2.22.0, commit unknown, built at unknown
※echo ${GOPATH}/bin
などにパスは通しておく。
概要
引用:
https://grpc-ecosystem.github.io/grpc-gateway/#getting-started
Write and download Proto files
図書館の本を借りるサービスをGolangで実装します。このサービスにRestful APIとgRPC APIを実装します。
mkdir sample-book-lending
go mod init sample-book-lending
protofileを格納するディレクトリです。
mkdir proto
cd proto
Copy google/api/annotations.proto
and google/api/http.proto
Copy google/api/annotations.proto
and google/api/http.proto
Proxy Serverの実装のためのProtofileをコピーします。
mkdir -p google/api
cd google/api
curl -O https://raw.githubusercontent.com/googleapis/googleapis/refs/heads/master/google/api/annotations.proto
curl -O https://raw.githubusercontent.com/googleapis/googleapis/refs/heads/master/google/api/http.proto
mv *.proto google/api/
*.proto作成
book.proto
syntax = "proto3";
option go_package = "./pkg/grpc";
message Book {
string title = 1; //
}
account.proto
syntax = "proto3";
// for proxy
import "google/api/annotations.proto";
import "book.proto";
option go_package = "./pkg/grpc";
package myapp;
// 本を借りるためのAccount
message Account {
string name = 1;
}
message AccountInfo {
string name = 1;
}
message BorrowRequest {
Account account = 1;
Book book = 2;
}
message BorrrowResponse {
Account account = 1;
Book book = 2;
}
// サービスの定義
service LendingBooksService {
rpc SendBorrow (BorrowRequest) returns (BorrrowResponse);
rpc AccountInfo (Account) returns (Account) {
option (google.api.http) = {
post: "/v1/accountinfo/books"
body: "*"
};
}
}
pwd
sample-book-lending/proto
proto % tree
.
├── account.proto
├── book.proto
└── google
└── api
├── annotations.proto
└── http.proto
*.protoをビルド
protocを実行します。※bufを使用する方法もありますが今回はprotocを使用します。
protoc -I ./proto \
--go_out=./pkg/grpc --go_opt paths=source_relative \
--go-grpc_out ./pkg/grpc --go-grpc_opt paths=source_relative \
--grpc-gateway_out ./pkg/grpc --grpc-gateway_opt paths=source_relative \
./proto/*.proto
Server実装
ビルドした*.protoを使用してServerにメソッドを実装します。
下記のようにSever Processを実装するためにmain.go
を作成します。
sample-book-lending % tree ./cmd
./cmd
├── client
└── server
└── main.go
server/main.go
package main
import (
"context"
"fmt"
"google.golang.org/grpc/reflection"
hellopb "sample-book-lending/pkg/grpc"
"log"
"os"
"os/signal"
// (一部抜粋)
"google.golang.org/grpc"
"net"
// for proxy
"google.golang.org/grpc/credentials/insecure"
"github.com/grpc-ecosystem/grpc-gateway/v2/runtime"
"net/http"
)
type myServer struct {
hellopb.UnimplementedLendingBooksServiceServer
}
// account.protoの`service`に定義したメソッドの実装
// 本を借りるためのメソッド
func (s *myServer) SendBorrow(ctx context.Context, req *hellopb.BorrowRequest) (*hellopb.BorrrowResponse, error) {
// リクエストからnameフィールドを取り出して
// "Hello, [名前]!"というレスポンスを返す
return &hellopb.BorrrowResponse{
Account: &hellopb.Account{Name: req.Account.Name},
Book: &hellopb.Book{Title: req.Book.Title},
}, nil
}
// account.protoの`service`に定義したメソッドの実装
// アカウントの貸与状態を知るためのRestful API
func (s *myServer) AccountInfo(ctx context.Context, req *hellopb.Account) (*hellopb.Account, error) {
// リクエストからnameフィールドを取り出して
// "Hello, [名前]!"というレスポンスを返す
return &hellopb.Account{
Name: req.GetName(),
}, nil
}
// 自作サービス構造体のコンストラクタを定義
func NewMyServer() *myServer {
return &myServer{}
}
func main() {
// 1. 8080番portのLisnterを作成
port := 8080
listener, err := net.Listen("tcp", fmt.Sprintf(":%d", port))
if err != nil {
panic(err)
}
// 2. gRPCサーバーを作成
s := grpc.NewServer()
// 3. gRPCサーバーにGreetingServiceを登録
// 第二引数はinterfaceであるGreetingServiceServerのため、これのメソッドリストを実装した構造体がはいる。
hellopb.RegisterLendingBooksServiceServer(s, NewMyServer())
// x_numberはproxy serverのインスタンス作成と起動です。
// x_1. for proxy
// Create a client connection to the gRPC server we just started
// This is where the gRPC-Gateway proxies the requests
conn, err := grpc.NewClient(
"0.0.0.0:8080",
grpc.WithTransportCredentials(insecure.NewCredentials()),
)
gwmux := runtime.NewServeMux()
err = hellopb.RegisterLendingBooksServiceHandler(context.Background(), gwmux, conn)
if err != nil {
log.Fatalln("Failed to register gateway:", err)
}
gwServer := &http.Server{
Addr: ":8090",
Handler: gwmux,
}
// 4. サーバーリフレクションの設定
reflection.Register(s)
// 5. 作成したgRPCサーバーを、8080番ポートで稼働させる
go func() {
log.Printf("start gRPC server port: %v", port)
s.Serve(listener)
}()
// x_2. for proxy
log.Println("Serving gRPC-Gateway on http://0.0.0.0:8090")
log.Fatalln(gwServer.ListenAndServe())
// 6.Ctrl+Cが入力されたらGraceful shutdownされるようにする
quit := make(chan os.Signal, 1)
signal.Notify(quit, os.Interrupt)
<-quit
log.Println("stopping gRPC server...")
s.GracefulStop()
}
サーバーを起動
goを実行します。
cd cmd/server && go run main.go
2024/09/27 16:37:41 Serving gRPC-Gateway on http://0.0.0.0:8090
2024/09/27 16:37:41 start gRPC server port: 8080
Client
Restful APIとgRPC APIを実装しています。
リクエストを送信してみます。
call gRPC API
grpcurl -plaintext -d '{"account":{ "name": "Tanaka"},"book":{ "title": "赤毛のあん"}}' localhost:8080 myapp.LendingBooksService.SendBorrow
{
"account": {
"name": "Tanaka"
},
"book": {
"title": "赤毛のあん"
}
}
call Restful API
curl -X POST -k http://localhost:8090/v1/accountinfo/books -d '{"name": " Proxy Anpanman"}'
{"name":" Proxy Anpanman"}
Doc生成
Install protoc-gen-doc
protoc-gen-docをインストールしてください。
go install github.com/pseudomuto/protoc-gen-doc/cmd/protoc-gen-doc@latest
ドキュメント生成
protofileからドキュメントを生成します。コマンドでprofofileから仕様書を生成できるので、ドキュメント管理が楽です。
protoc -I ./proto --doc_out=html,index.html:./doc proto/*.proto
生成されたProtocol Documentationです。
今回の全コードはこちら。