はじめに
朝日ネットで開発をしています。muratamです。 あたらしいプロダクトをmicroservicesで作る…ということがあるかもしれない、ということで軽く触れてみたいと思います。 今回は、GoとgRPCを使ってみます。
microservices とは
micro、つまりちいさなサービスを複数作り、それらを組み合わせてひとつのサービスを提供するアーキテクチャのことです。 スイミーですね。 個々のサービスは単一の機能を提供する独立した存在で、それらがメッセージをやり取りすることで一体として動きます。 microservice architectureの採用例としてはNetflixがよく知られています。 microserviceとは逆に単一アプリケーションで提供している状態をmonolithic(一枚岩)といいます。
利点としては、高負荷なサービスのインスタンスだけを増やすことができる性能面のメリット、一部に障害が起きても部品を交換するように新しいインスタンスを投入して復旧できる可用性があります。また開発面でもコードが分離・独立しているのでメンテナンスしやすく、別々の小規模なチームで同時に開発を進めやすいという点があります。
逆に難しい点としてはサービス全体のコントロールでしょうか。 多くのサービスが動いているためそれらのデプロイや監視をどうするかというのは問題になります。
gRPCについて
ちいさなサービスが複数動いていて連携する、ということはサービス間の通信がとても重要になります。 ここでは通信手段として gRPC を使います。 grpc.io gRPCはGoogleが開発したRPCフレームワークです。高速で、多くのプログラム言語に対応しています。 RPCでやりとりするデータのシリアライズにはProtocol Buffersを使います。 Protocol Buffersで定義したものを各プログラム言語のソースコードに変換して、プログラムから利用できるようにします。
gRPCで通信してみる
ここでは単純なサーバとクライアントだけでやってみます。 クライアントが文字列を送るとサーバは文字数と単語数を返します。
gRPCの入手
Goをつかいます。 まずはGo向けのgRPCライブラリを入手
% go get -u google.golang.org/grpc
そして、Protocol Buffersをソースコードに変換するprotoc
を入手します。
https://github.com/protocolbuffers/protobuf/releases
また、Protocol BuffersのGo言語対応ランタイムも入手します。 1
% go get -u github.com/golang/protobuf/protoc-gen-go
Protocol Buffersの定義
$GOPATH/src/grpc01/
以下に作成していきます。
proto/wc.proto
にProtocol Buffers定義を書きます。
// grpc01/proto/wc.proto syntax = "proto3"; package wc; service Count { rpc WCount(Request) returns (Response) {} } message Request { string msg = 1; } message Response { int32 words = 1; int32 chars = 2; }
Count
というserviceを定義します。WCount
というメソッドを持っていて、Request
というメッセージを受け取ってResponse
というメッセージを返します。
これをprotoc
で変換します。
% protoc -I=proto/ --go_out=plugins=grpc:wc/ proto/wc.proto
変換結果がwc/wc.pb.go
に出力されました。
// Code generated by protoc-gen-go. DO NOT EDIT. // source: wc.proto package wc import proto "github.com/golang/protobuf/proto" ...
サーバの実装
先ほど生成したwc.pb.go
にはCountServer
のinterfaceができています。
wc.proto
で定義したWCount
がinterfaceに含まれています。
// CountServer is the server API for Count service. type CountServer interface { WCount(context.Context, *Request) (*Response, error) }
というわけでWCount
を実装したserverを作り、これをgRPCのサーバとして動かします。
サーバプログラムはserver/server.go
に作ることにします。
package main import ( "context" "fmt" "google.golang.org/grpc" "grpc01/wc" "net" "os" "regexp" ) func main() { listener, err := net.Listen("tcp", ":5300") if err != nil { fmt.Fprintf(os.Stderr, "net.Listen: %v\n", err) return } grpcSrv := grpc.NewServer() wc.RegisterCountServer(grpcSrv, &server{}) grpcSrv.Serve(listener) } type server struct{} var spaceRE, _ = regexp.Compile("[ \\s]+") func (s *server) WCount(ctx context.Context, req *wc.Request) (*wc.Response, error) { msg := req.Msg var numW, numC int32 numC = int32(len([]rune(msg))) words := spaceRE.Split(msg, -1) numW = int32(len(words)) res := &wc.Response{ Chars: numC, Words: numW, } return res, nil }
クライアントの実装
wc.pb.go
にはクライアントのinterfaceもあります。
こちらもWCount
を含んでおり、さらにそれを実装したcountClient
が定義されています。
// CountClient is the client API for Count service. // // For semantics around ctx use and closing/ending streaming RPCs, please refer to https://godoc.org/google.golang.org/grpc#ClientConn.NewStream. type CountClient interface { WCount(ctx context.Context, in *Request, opts ...grpc.CallOption) (*Response, error) } type countClient struct { cc *grpc.ClientConn } func NewCountClient(cc *grpc.ClientConn) CountClient { return &countClient{cc} } func (c *countClient) WCount(ctx context.Context, in *Request, opts ...grpc.CallOption) (*Response, error) { ... }
クライアントプログラムからはそのクライアントからWcount
を呼び出します。
クライアントプログラムはclient/client.go
に作ることにします。
package main import ( "context" "fmt" "google.golang.org/grpc" "grpc01/wc" "os" ) func main() { args := os.Args conn, err := grpc.Dial("127.0.0.1:5300", grpc.WithInsecure()) if err != nil { fmt.Fprintf(os.Stderr, "grpc.Dial: %v\n", err) return } defer conn.Close() client := wc.NewCountClient(conn) for i, v := range args[1:] { req := &wc.Request{Msg: v} res, err := client.WCount(context.Background(), req) if err != nil { fmt.Fprintf(os.Stderr, "WCount: %v for %v\n", err, v) continue } fmt.Fprintf(os.Stdout, "%d\t%v: %d characters, %d words\n", i+1, v, res.Chars, res.Words) } }
動かす
サーバを立てて
% go build -o ./wcserver server/server.go
% ./wcserver &
クライアントを動かすと結果を返してくれます。
% go build -o ./wcclient client/client.go % ./wcclient "water blue new world" "The quick brown fox jumps over the lazy dog" "いろはにほへと" 1 water blue new world: 20 characters, 4 words 2 The quick brown fox jumps over the lazy dog: 43 characters, 9 words 3 いろはにほへと: 7 characters, 1 words
今回のまとめ
gRPCを使って通信をしてみました。 次回はこのgRPCを使って通信する、小規模なmicoservices構成のwebサービスを作ってみたいと思います。
採用情報
朝日ネットでは新卒採用・キャリア採用を行っております。
-
10/12追記↩