Googleが作ったRPCフレームワークgRPCを使ってみた
grpcA high performance, open source, general RPC framework that puts mobile and HTTP/2 first.
What is gRPC?
gRPCを使うと、クライアントアプリケーションは直接ローカルのオブジェクトのように、他のマシンのサーバーアプリケーションのメソッドを呼ぶことができ、 分散したアプリケーションやサービスを簡単に作ることができる。 多くのRPCシステムと同様にgRPCはサービスを定義し、リモートから呼べるメソッドとそのパラメーターおよび返り値の型を記述するようになっている。 サーバーサイドではインタフェースを実装し、クライアントからの呼び出しをハンドリングするgRPCサーバーを実行する。 クライアントサイドではサーバーと同じメソッドを提供するスタブを持っている。
gRPCクライアントとサーバーは様々な環境同士でやり取りすることができ、いくつもの言語でサポートされている。 そのため例えば、gRPCサーバーをJavaでクライアントをGoやPython、Rubyで作るのも可能だ。 加えて、最新のGoodle APIにはgRPCのインタフェースが存在するので、これらをアプリケーションに組み込むのも容易にできる。
Protobuf
デフォルトではgRPCはprotobuf(protocol buffers)でやり取りする。 protobufというのは、 Googleによるオープンソースのシリアライズフォーマット。
今回作るのは、同じ文字列を返すだけのEchoサーバーで、コードはここにある。 以下のprotoファイルでは、 Echo というサービスは RetEcho というメソッドを含み、 これは文字列 say を含む EchoRequest に対して、文字列 ret を含む EchoReply を返すということを表している。
syntax = "proto3";
option java_package = "net.sambaiz.trygrpc.protos";
package protos;
service Echo {
rpc RetEcho (EchoRequest) returns (EchoReply) {}
}
message EchoRequest {
string say = 1;
}
message EchoReply {
string ret = 1;
}
これをprotocでコンパイルするとecho.pb.goのようなコードが生成される。
$ brew install --devel protobuf # install Install Protocol Compiler v3.0.0-beta-2
$ go get -u github.com/golang/protobuf/protoc-gen-go # Install Go Protobuf Runtime Installation
$ protoc --go_out=plugins=grpc:protos/. protos/*.proto
サーバー
$ go get google.golang.org/grpc
protoファイルに書いたRetEchoを実装し、サーバーを立ち上げる。
package main
import (
"golang.org/x/net/context"
"google.golang.org/grpc"
"net"
"logging"
pb "github.com/sambaiz/try-gRPC/protos"
)
const (
port = ":50051"
)
type server struct{}
func (s *server) RetEcho(ctx context.Context, in *pb.EchoRequest) (*pb.EchoReply, error) {
return &pb.EchoReply{Ret: in.Say}, nil
}
func main() {
lis, err := net.Listen("tcp", port)
if err != nil {
log.Fatalf("failed to listen: %v", err)
}
s := grpc.NewServer()
pb.RegisterEchoServer(s, &server{})
log.Printf("server start localhost%s", port)
s.Serve(lis)
}
GoのgRPC ServerのInterceptor(recovery/auth/zap/prometheus) - sambaiz-net
クライアント
サーバーに接続すると、他のメソッドと同じようにサーバー側の RetEcho() メソッドを呼び出すことができるようになる。
package main
import (
"logging"
"google.golang.org/grpc"
"golang.org/x/net/context"
pb "github.com/sambaiz/try-gRPC/protos"
)
const (
address = "localhost:50051"
)
func main() {
conn, err := grpc.Dial(address, grpc.WithInsecure())
if err != nil {
log.Fatalf("did not connect: %v", err)
}
defer conn.Close()
c := pb.NewEchoClient(conn)
r, err := c.RetEcho(context.Background(), &pb.EchoRequest{Say: "hello"})
if err != nil {
log.Fatalf("Error: %v", err)
}
log.Printf("Return: %s", r.Ret)
}
最新のprotoファイルを共有すれば、どんな言語で書いても自分で型を定義したり、呼び出すロジックを書く必要がないし、 インタフェースが変わったときにコードレベルでエラーに気づけるのは通常のAPIリクエストと比較して良い点だと思う。 ロジックが複数のサーバーに渡る場合の負担を最小限にできるため、サービスを小さく作るマイクロサービスアーキテクチャで使われるが、性能や管理のしやすさなどの面でもメリット/デメリットはある。