システム構築にgRPCを導入した話
golang.tokyo #8
28 August 2017
dice_zu(daisuzu)
dice_zu(daisuzu)
Twitter: @dice_zu
GitHub: https://github.com/daisuzu
Blog: http://daisuzu.hatenablog.com
Google製のRPC
以下のような契約管理システム
1. クライアントからオーダーを受け付けて、
2. 外部のシステムに送信すると、
3. 非同期で処理結果が返ってくるので、
4. それをクライアントに返す。
' :: ___________ ' +--------+ :: ( ) ' | sender | ----::-- ( ) ' +--------+ :: ( ) ' / :: ( ) +--------+ ' +--------+ +------------+ :: ( external ) | client | ---'--- | server | - | relay node | :: ( service ) +--------+ ' +--------+ +------------+ :: ( ) ' \ :: ( ) ' +----------+ :: ( ) ' | receiver | --::-- ( ) ' +----------+ :: (___________)
service Contract { rpc Create (CreateRequest) returns (CreateResponse) {} rpc Update (UpdateRequest) returns (UpdateResponse) {} rpc Activate (ActivateRequest) returns (ActivateResponse) {} rpc XXX (XXXRequest) returns (XXXResponse) {} rpc YYY (YYYRequest) returns (YYYResponse) {} rpc ZZZ (ZZZRequest) returns (ZZZResponse) {} }
JSON SchemaやSwaggerと比べると、
想定クライアントが
だったので全体的にgRPC対応のモダンなシステムにしたかった
OK
NG
結局 grpc-gateway を使ってRESTインターフェースも提供した
メッセージのstructとサービスのinterfaceのみ
余計なものが含まれない
type service struct{} func (s *service) Create(ctx context.Context, in *pb.CreateRequest) (*pb.CreateResponse, error) { return &pb.CreateResponse{}, nil } func main() { s := grpc.NewServer() pb.RegisterContractServer(s, &service{}) l, _ := net.Listen("tcp", ADDR) s.Serve(l) }
生成されたコード (pbパッケージ) からクライアントを生成するツールを作った
(今は改善されているが) 何も対策をしてないとサーバが落ちる
レスポンスの生成に気をつけつつ、interceptorでハンドリング
gRPCは常時接続だが、無通信の間に接続先コンテナが替わってしまうことがあった
この時、次のリクエストを送信するまでエラーにならないので発見が遅れた
クライアント側の grpc.WithDialer でTCPのkeepaliveを設定して回避
興味がある人は是非導入を!
そして知見の共有をお願いしますm(_ _)m