はじめに
この記事はユニークビジョン株式会社 Advent Calendar 2018の11日目の記事です。
コンテナを中心とした開発が主流になっていくなかで、コンテナ間の通信はどうするのかという問題がつきまといます。
普通にAPIとして実装しても良いと思いますが、Googleが開発しているgRPCを活用すれば通信方法やAPIのドキュメントの準備の手間を軽減したり、HTTP/2を利用した良好なパフォーマンスなど様々なメリットを享受できます。
社内への宣伝を兼ねて、今回gRPCのチュートリアルをやってみたいと思います。
gRPC
Google社が開発しているRPCフレームワークです。.protoを定義することで、そこから様々な言語のコードを生成し、サーバにメソッドを定義することで、異なる言語間で通信が可能となります。
公式のドキュメントに対応言語のチュートリアルなどがまとまっており、今回はここ1を参考にしつつ実装していきます。
Hello, World
準備
#### 環境
- Go(今回は1.10を使用します)
- Goのセットアップに関してはGo公式のドキュメントやその他の記事を参照してください
- $GOPATH定義までしてあるといいとおもいます
ライブラリ
gRPCと併せて今回goで実装するのでGoPluginも
$ go get -u google.golang.org/grpc
$ go get -u github.com/golang/protobuf/protoc-gen-go
ディレクトリ構成
適当なディレクトリ($GOPATH以下が望ましい)に以下のような形で準備します。
.
├── client
│ └── main.go
├── hellogrpc
│ └── hellogrpc.proto
└── server
└── main.go
.protoの定義
今回はhellogrpcディレクトリ以下に、サーバ - クライアント間のインタフェースを定義するprotoファイルを設置します。
このprotoファイルを元にgRPCがサポートしている各言語の実装を生成します。
syntax = "proto3";
option java_multiple_files = true;
option java_package = "io.tokikokoko.hellogrpc.server";
option java_outer_classname = "HelloGrpc";
package HelloGrpc;
// The greeting service definition.
service HelloGrpc {
// Sends a greeting
rpc GreetServer (GreetRequest) returns (GreetMessage) {}
}
message GreetRequest {
string name = 1;
}
message GreetMessage {
string msg = 1;
}
上記のファイルで注目してほしいのはpackage HelloGrpc;
以下で、
// The greeting service definition.
service HelloGrpc {
// Sends a greeting
rpc GreetServer (GreetRequest) returns (GreetMessage) {}
}
service
内で今回実装するgRPCサーバにどのようなメソッドを実装するのかを定義します。
rpc
に続く文がserviceのメソッドとなり、ここではGreetServer
というメソッドを定義しています。これはGreetRequest
型のmessage
を引数としてGreetMessage
型のmessage
を返すという定義です。
message GreetRequest {
string name = 1;
}
message GreetMessage {
string msg = 1;
}
message
に続く文でメッセージ型を定義しており、構造体のように扱えます。
ここでは
-
string
型のname
を持ったGreetRequest
-
string
型のmsg
を持ったGreetMessage
を定義しています。
.protoをコンパイル
定義した.protoファイルはコンパイルすることで、各言語の実装を出力します。出力されたコードをimport
することで定義したインタフェースを実装、呼び出すことができます。
今回定義したprotoファイルで、Go言語のコードを出力するためには、
$ protoc -I hellogrpc/ hellogrpc/hellogrpc.proto --go_out=plugins=grpc:hellogrpc
を実行することで、hellogrpc/hellogrpc.pb.go
が出力されます。
サーバとクライアントを実装する
出力されたコードをimportして、実際にサーバとクライアントを実装していきます。
package main
import (
"context"
"fmt"
"net"
"google.golang.org/grpc"
"log"
// .protoから生成されたコードをimportしている
// 今回は筆者の$GOPATH内に作成したので適宜プロジェクトを作成したパスに合わせる
pb "github.com/tokikokoko/hello_grpc/hellogrpc"
)
// gRPC server struct
type server struct {
}
// .protoで定義したGreetServerを定義している
func (s *server) GreetServer(ctx context.Context, p *pb.GreetRequest) (*pb.GreetMessage, error) {
log.Printf("Request from: %s", p.Name)
return &pb.GreetMessage{Msg: fmt.Sprintf("Hello, %s. ", p.Name)}, nil
}
func main() {
// gRPC
port := 10000
lis, err := net.Listen("tcp", fmt.Sprintf("localhost:%d", port))
if err != nil {
log.Fatalf("failed to listen: %v", err)
}
log.Printf("Run server port: %d", port)
grpcServer := grpc.NewServer()
// メソッドを定義
pb.RegisterHelloGrpcServer(grpcServer, &server{})
// gRPCサーバを公開
if err := grpcServer.Serve(lis); err != nil {
log.Fatalf("failed to serve: %v", err)
}
}
package main
import (
"context"
"log"
"time"
"google.golang.org/grpc"
// .protoから生成されたコードをimportしている
// 今回は筆者の$GOPATH内に作成したので適宜プロジェクトを作成したパスに合わせる
pb "github.com/tokikokoko/hello_grpc/hellogrpc"
)
const (
address = "localhost:10000"
)
func main() {
// gRPCサーバへのconnectionを作成
conn, err := grpc.Dial(address, grpc.WithInsecure())
if err != nil {
log.Fatalf("did not connect: %v", err)
}
// connection終了処理
// 関数終了後に実行される
defer conn.Close()
c := pb.NewHelloGrpcClient(conn)
name := "ほげ太郎"
// タイムアウトを設定
ctx, cancel := context.WithTimeout(context.Background(), time.Second)
defer cancel()
// .protoで定義したサーバのGreetServerを呼び出している
r, err := c.GreetServer(ctx, &pb.GreetRequest{Name: name})
if err != nil {
log.Fatalf("could not greet: %v", err)
}
log.Printf("Response: %s", r.Msg)
}
実行結果
$ go run server/main.go
2018/12/11 00:37:24 Run server port: 10000
2018/12/11 00:37:26 Request from: ほげ太郎
$ go run client/main.go
2018/12/11 00:37:26 Response: Hello, ほげ太郎.
まとめ
超簡単なgRPCサーバ&クライアントを実装しました。gRPCの概要を理解してもらえれば幸いです。超ぬるい記事ですが、初投稿なのでお許しくださいorz
明日も私が担当です。CI/CDするとか言っていましたがgRPCの導入の話が存外に長くなってしまったので、今日に引き続きgRPCのstreamを使った簡素なチャットツールを実装しようと思います。