#はじめに
「gRPCを使ってみたものの、REST-APIは使えないのか?」
「gRPCに加えて、REST-APIを使いたい」
「gRPCで作ったものを簡単に検証したい」
と思って調べると、grpc-gateway
というものがありましたので、使ってみます。
https://github.com/grpc-ecosystem/grpc-gateway
#gRPC
gRPCについては、別途記事を書いていますので、こちらを参照してください。
https://qiita.com/ryu3/items/d5cc28a412a06e17ef98
#grpc-gatewayの概要
grpc-gatewayはgRPCで書かれたAPIを、JSON over HTTPのAPIに変換してくれます。コード生成器として機能し、ある種のリバースプロキシサーバーを生成します。下の図をご覧ください。
#インストール
下記コマンドでインストールしてください。
$ go get -u -v github.com/grpc-ecosystem/grpc-gateway/protoc-gen-grpc-gateway
$ go get -u -v github.com/grpc-ecosystem/grpc-gateway/protoc-gen-swagger
$ go get -u -v github.com/golang/protobuf/protoc-gen-go
#使ってみよう
##1.gRPCサービスを定義
syntax = "proto3";
package example;
import "google/api/annotations.proto";
service HelloWorldService {
rpc SayHello (HelloRequest) returns (HelloReply) {
}
rpc GetUser(GetUserRequest) returns (User) {
}
rpc CreateUser(CreateUserRequest) returns (User) {
}
}
message HelloRequest {
string name = 1;
}
message HelloReply {
string message = 1;
}
message GetUserRequest {
string id = 1;
}
message CreateUserRequest {
string name = 1;
}
message User {
string id = 1;
string name = 2;
}
##2.google.api.http
を追加します。
syntax = "proto3";
package example;
import "google/api/annotations.proto";
service HelloWorldService {
rpc SayHello (HelloRequest) returns (HelloReply) {
option (google.api.http) = {
get: "/v1/example/sayhello/{name}"
};
}
rpc GetUser(GetUserRequest) returns (User) {
option (google.api.http) = {
get: "/v1/example/users/{id}"
};
}
rpc CreateUser(CreateUserRequest) returns (User) {
option (google.api.http) = {
post: "/v1/example/users"
body: "*"
};
}
}
message HelloRequest {
string name = 1;
}
message HelloReply {
string message = 1;
}
message GetUserRequest {
string id = 1;
}
message CreateUserRequest {
string name = 1;
}
message User {
string id = 1;
string name = 2;
}
##3.gRPC stub を生成する
protoc
コンパイラを使って、.proto
ファイルをビルドして、.pb.go
ファイルを作成します
protoc -I/usr/local/include -I. \
-I$GOPATH/src \
-I$GOPATH/src/github.com/grpc-ecosystem/grpc-gateway/third_party/googleapis \
--go_out=plugins=grpc:. \
service.proto
path/to/your_service.pb.go
が生成されます。
##4.reverse-proxy
を生成する
protoc
コンパイラを使って、.proto
ファイルをビルドして、.pb.gw.go
ファイルを作成します
protoc -I/usr/local/include -I. \
-I$GOPATH/src \
-I$GOPATH/src/github.com/grpc-ecosystem/grpc-gateway/third_party/googleapis \
--grpc-gateway_out=logtostderr=true:. \
service.proto
##5. Gateway / サーバの作成
gateway / サーバのを下記のように作成します。
package main
import (
"context"
"log"
"net"
"google.golang.org/grpc"
pb "../proto"
)
const (
port = ":5001"
)
// server is used to implement HelloWorldServer.
type server struct{}
// SayHello implements HelloWorldServer
func (s *server) SayHello(ctx context.Context, in *pb.HelloRequest) (*pb.HelloReply, error) {
log.Printf("Received: %v", in.Name)
return &pb.HelloReply{Message: "Hello " + in.Name}, nil
}
// GetUser
func (s *server) GetUser(ctx context.Context, in *pb.GetUserRequest) (*pb.User, error) {
log.Printf("Received: %v", in.Id)
return &pb.User{
Id: in.Id,
Name: "SampleUser"}, nil
}
// CreateUser
func (s *server) CreateUser(ctx context.Context, in *pb.CreateUserRequest) (*pb.User, error) {
log.Printf("Received: %v", in.Name)
return &pb.User{
Id: "123",
Name: in.Name}, nil
}
func main() {
lis, err := net.Listen("tcp", port)
if err != nil {
log.Fatalf("failed to listen: %v", err)
}
s := grpc.NewServer()
pb.RegisterHelloWorldServiceServer(s, &server{})
if err := s.Serve(lis); err != nil {
log.Fatalf("failed to serve: %v", err)
}
}
package main
import (
"flag"
"fmt"
"net/http"
"github.com/golang/glog"
"golang.org/x/net/context"
"github.com/grpc-ecosystem/grpc-gateway/runtime"
"google.golang.org/grpc"
gw "../proto"
)
func run() error {
ctx := context.Background()
ctx, cancel := context.WithCancel(ctx)
defer cancel()
mux := runtime.NewServeMux()
opts := []grpc.DialOption{grpc.WithInsecure()}
endpoint := fmt.Sprintf("localhost:5001")
err := gw.RegisterHelloWorldServiceHandlerFromEndpoint(ctx, mux, endpoint, opts)
if err != nil {
return err
}
return http.ListenAndServe(":5000", mux)
}
func main() {
flag.Parse()
defer glog.Flush()
if err := run(); err != nil {
glog.Fatal(err)
}
}
##動作確認
gatewayとサーバを実行してみましょう。
go run greeter_gateway/main.go
別のターミナルを開いて、下記を実行します。
go run greeter_server/main.go
curl
コマンドでREST APIを実行してみましょう。
期待通りにデータを取得できています。
ユーザ名(nakata)を入れて、sayhello
へGETします。
curl -X GET http://localhost:5000/v1/example/sayhello/nakata
{"message":"Hello nakata"}
ユーザID(10)を入れて、users
へGETします。
curl -X GET http://localhost:5000/v1/example/users/10
{"id":"10","name":"SampleUser"}
ユーザ名(nakata)を入れて、users
へPOSTします。
curl -X POST http://localhost:5000/v1/example/users -d '{"name":"nakata"}'
{"id":"123","name":"nakata"}
#おわりに
gRPCに対応するRESTful APIを自動生成しました。
gRPCサーバの実装をcurlコマンドで確認することができました。