0
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

grpc-gatewayでRESTful API/gRPC API実装

Last updated at Posted at 2024-09-27

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です。

今回の全コードはこちら

0
0
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
0
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?