Upgrade to Pro — share decks privately, control downloads, hide ads and more …

Go+gPRCとprotocプラグイン / go-grpc-and-protoc-plugins

yoshd
April 18, 2019

Go+gPRCとprotocプラグイン / go-grpc-and-protoc-plugins

2019/04/18 オタクが最新技術を追うライトニングトークイベント LT用資料

https://github.com/yoshd/go-grpc-lt
https://github.com/yoshd/protoc-gen-friends

yoshd

April 18, 2019
Tweet

Other Decks in Programming

Transcript

  1. 2019/4/18 reveal.js localhost:8000/?print-pdf 3/34 話すこと 話すこと gRPC とは Go +

    gPRC の簡単な例 便利なprotoc プラグイン protoc-gen-doc protoc-gen-grpc-gateway protoc-gen-swagger protoc プラグインの実装方法
  2. 2019/4/18 reveal.js localhost:8000/?print-pdf 4/34 gPRC とは gPRC とは Google 製のOSS

    のRPC(Remote Procedure Call) フレームワーク IDL(Interface Definition Language) ・シリアライズフォーマットに Protocol Buffers を使用 インターフェイスを定義した ファイルからサーバー側と クライアント側の様々な言語のソースコードを生成できる 通信プロトコルはHTTP/2 https://grpc.io/
  3. 2019/4/18 reveal.js localhost:8000/?print-pdf 6/34 やること やること Go でgPRC サーバー・クライアントを実装する 1.

    と をインストール 2. Protocol Buffers でインターフェイスを定義 3. インターフェイスを定義した ファイルからコード生成 4. 3 で生成されたコードを使ってgRPC サーバーとクライアントを実装 今回の例のソースはこちらにあります https://github.com/yoshd/go-grpc-lt
  4. 2019/4/18 reveal.js localhost:8000/?print-pdf 7/34 と と をインストール をインストール Protocol Buffers

    で定義された ファイルを読み取って 対応するクラスや構造体などを生成するコンパイラ Go のコードを生成するために必要な のプラグイン
  5. 2019/4/18 reveal.js localhost:8000/?print-pdf 8/34 と と をインストール をインストール 公式の Docker

    image で 公式の Docker image で と と 両方使えます 両方使えます Docker を使わない場合 Docker を使わない場合 protoc protoc-gen-go $ docker pull grpc/go $ wget https://github.com/protocolbuffers/protobuf/releases/download/<version>/protoc-<version>-<plat $ go get -u github.com/golang/protobuf/protoc-gen-go
  6. 2019/4/18 reveal.js localhost:8000/?print-pdf 9/34 Protocol Buffers でインターフェイスを定義 Protocol Buffers でインターフェイスを定義

    syntax = "proto3"; // Go 生成 場合 名 option go_package = "pb"; // Sample 定義 service Sample { // Sample Hello gRPC 定義 // 引数 HelloRequest 受 取 HelloResponse 返 rpc Hello (HelloRequest) returns (HelloResponse) {} } // HelloRequest 定義 message HelloRequest { // string型 name 持 string name = 1; } // HelloResponse 定義 message HelloResponse { // string型 message 持 string message = 1; }
  7. 2019/4/18 reveal.js localhost:8000/?print-pdf 10/34 インターフェイスを定義した インターフェイスを定義した ファイルからコード生成 ファイルからコード生成 が生成される 生成されたコードには下記が含まれる

    や に対応する構造体 サーバー側で使用する のインターフェイス クライアントが の を呼び出すための実装 など $ protoc sample.proto --go_out=plugins=grpc:pb https://github.com/yoshd/go-grpc-lt/blob/master/pb/sample.pb.go
  8. 2019/4/18 reveal.js localhost:8000/?print-pdf 11/34 生成されたコードを使って gRPC サーバーを実装 生成されたコードを使って gRPC サーバーを実装

    package main import ( "context" "fmt" "net" "github.com/yoshd/go-grpc-lt/pb" "google.golang.org/grpc" ) type server struct{} // 生成 定義 側 interface 実装 func (s *server) Hello(ctx context.Context, in *pb.HelloRequest) (*pb.HelloResponse, error) { return &pb.HelloResponse{Message: fmt.Sprintf("Hello %s!", in.GetName())}, nil } func main() { lis, err := net.Listen("tcp", "localhost:13009") if err != nil { panic(err) } s := grpc.NewServer() pb.RegisterSampleServer(s, &server{}) s.Serve(lis) }
  9. 2019/4/18 reveal.js localhost:8000/?print-pdf 12/34 生成されたコードを使って gRPC クライアントを 生成されたコードを使って gRPC クライアントを

    実装 実装 package main import ( "context" "fmt" "github.com/yoshd/go-grpc-lt/pb" "google.golang.org/grpc" ) func main() { conn, err := grpc.Dial("localhost:13009", grpc.WithInsecure()) if err != nil { panic(err) } defer conn.Close() // 生成 使用 簡単 gPRC 呼 c := pb.NewSampleClient(conn) req := pb.HelloRequest{Name: "Yoshd"} res, err := c.Hello(context.Background(), &req) if err != nil { panic(err) } fmt.Println(res.Message) }
  10. 2019/4/18 reveal.js localhost:8000/?print-pdf 13/34 動かしてみる 動かしてみる サーバー側 クライアント側 超簡単!!! #

    生成 使 gRPC 実装 書 実行 $ go run server/main.go # 生成 使 gRPC 実装 書 実行 $ go run client/main.go Hello Yoshd!
  11. 2019/4/18 reveal.js localhost:8000/?print-pdf 14/34 便利な protoc プラグイン 便利な protoc プラグイン

    を使えばProtocol Buffers で定義されたスキーマを読み取って、簡単 に様々な言語のコード生成できることはおわかり頂けたと思う でもせっかくIDL で定義してるなら、gRPC のAPI ドキュメント等もスキーマ から生成したいなという気持ちになりますよね? プラグインを使えばできます!
  12. 2019/4/18 reveal.js localhost:8000/?print-pdf 15/34 gRPC のドキュメントを生成する gRPC のドキュメントを生成する プラグイン プラグイン

    インストール 呼び出し方は コマンド実行時に下記オプションを指定するだけ $ go get -u github.com/pseudomuto/protoc-gen-doc/cmd/protoc-gen-doc $ protoc --doc_out=<OUT_DIR> --doc_opt=<FORMAT>|<TEMPLATE_FILENAME>,<OUT_FILENAME>
  13. 2019/4/18 reveal.js localhost:8000/?print-pdf 16/34 gRPC のドキュメントを生成する gRPC のドキュメントを生成する プラグイン プラグイン

    先ほど使った ファイルにドキュメント用のコメントをつけて ( なくてもいい) ドキュメントを生成してみる syntax = "proto3"; option go_package = "pb"; // Sample 挨拶 得意 service Sample { // Hello 返 rpc Hello (HelloRequest) returns (HelloResponse) {} } // message HelloRequest { // 名前 入 string name = 1; } // message HelloResponse { // Hello, <NAME> 返 string message = 1; }
  14. 2019/4/18 reveal.js localhost:8000/?print-pdf 19/34 その他の便利な その他の便利な プラグイン プラグイン protoc-gen-go 冒頭でも触れましたが、実はGo

    のソースコードを生成するためにはこ のプラグインが必要 protoc-gen-grpc-gateway RESTful JSON API をgRPC に変換するプロキシのソースコードを生成する 簡単にHTTP リクエストをgRPC のリクエストにマッピングできる ファイルにgrpc-gateway 用にREST API のパスやHTTP メソッド等を 記述する必要がある protoc-gen-swagger とセットで使う で生成したRESTful JSON API のための Swagger 用JSON を生成する protoc-gen-govalidators ファイルにバリデーション用の記述をすることで、バリデーシ ョン用コードを生成できる
  15. 2019/4/18 reveal.js localhost:8000/?print-pdf 20/34 と と を使う を使う 下記のように ファイルを修正

    syntax = "proto3"; option go_package = "pb"; import "google/api/annotations.proto"; // Sample 挨拶 得意 service Sample { // Hello 返 rpc Hello (HelloRequest) returns (HelloResponse) { option (google.api.http) = { post: "/v1/sample/hello" body: "*" }; } } // message HelloRequest { // 名前 入 string name = 1; } // message HelloResponse { // Hello, <NAME> 返 string message = 1; }
  16. 2019/4/18 reveal.js localhost:8000/?print-pdf 21/34 と と を使う を使う プラグインインストール コード生成

    $ go get -u github.com/grpc-ecosystem/grpc-gateway/protoc-gen-grpc-gateway $ go get -u github.com/grpc-ecosystem/grpc-gateway/protoc-gen-swagger $ protoc \ -I$GOPATH/src/github.com/grpc-ecosystem/grpc-gateway/third_party/googleapis \ -I. \ --grpc-gateway_out=./pb \ --swagger_out=./swagger \ sample.proto
  17. 2019/4/18 reveal.js localhost:8000/?print-pdf 22/34 grpc-gateway で REST API -> gPRC

    のプロキシを立てる grpc-gateway で REST API -> gPRC のプロキシを立てる package main import ( "context" "net/http" "github.com/grpc-ecosystem/grpc-gateway/runtime" gw "github.com/yoshd/go-grpc-lt/pb" "google.golang.org/grpc" ) func main() { ctx := context.Background() ctx, cancel := context.WithCancel(ctx) defer cancel() mux := runtime.NewServeMux() opts := []grpc.DialOption{grpc.WithInsecure()} // 生成 使用 err := gw.RegisterSampleHandlerFromEndpoint(ctx, mux, "localhost:13009", opts) if err != nil { panic(err) } http.ListenAndServe(":8080", mux) }
  18. 2019/4/18 reveal.js localhost:8000/?print-pdf 23/34 grpc-gateway で REST API -> gPRC

    のプロキシを立てる grpc-gateway で REST API -> gPRC のプロキシを立てる 実行 grpc-gateway 経由でリクエストしてみる 超簡単!!! $ go run gateway/main.go $ curl localhost:8080/v1/sample/hello -d '{"name":"hoge"}' {"message":"Hello hoge!"}
  19. 2019/4/18 reveal.js localhost:8000/?print-pdf 24/34 grpc-gateway 用の Swagger grpc-gateway 用の Swagger

    先ほど で生成されたJSON は Swagger UI でみることができる
  20. 2019/4/18 reveal.js localhost:8000/?print-pdf 25/34 protoc プラグインを実装する protoc プラグインを実装する ここまで聞いて、ほとんどのエンジニアは 以下のようなことを思ったのではないでしょうか。

    既存の プラグインが便利なのはわかった でもエンジニアなら普通自分でプラグイン作るよね? 大人の事情でExcel にAPI 仕様を書かなければいけないんだけど protobuf スキーマからテスト用のコードも生成できるのでは? 何らかの共通処理をprotobuf スキーマを読み取って自動生成したい など 大丈夫、 protoc プラグインは簡単に作れます 大丈夫、 protoc プラグインは簡単に作れます
  21. 2019/4/18 reveal.js localhost:8000/?print-pdf 28/34 標準入力を受け取り parse する 標準入力を受け取り parse する

    package main import ( "io" "io/ioutil" "os" "github.com/golang/protobuf/proto" plugin "github.com/golang/protobuf/protoc-gen-go/plugin" ) // 標準入力 受 // plugin.CodeGeneratorRequest protoc 標準入力 結果 func parse(r io.Reader) (*plugin.CodeGeneratorRequest, error) { buf, err := ioutil.ReadAll(r) if err != nil { return nil, err } var req plugin.CodeGeneratorRequest if err = proto.Unmarshal(buf, &req); err != nil { return nil, err } return &req, nil } ...
  22. 2019/4/18 reveal.js localhost:8000/?print-pdf 29/34 何らかの処理をする 何らかの処理をする import ( "fmt" "github.com/golang/protobuf/proto"

    plugin "github.com/golang/protobuf/protoc-gen-go/plugin" ) // 情報 受 取 何 処理 出力 内容 作成 func process(req *plugin.CodeGeneratorRequest) *plugin.CodeGeneratorResponse { files := make(map[string]*descriptor.FileDescriptorProto) for _, f := range req.ProtoFile { files[f.GetName()] = f // 読 取 .proto } var res plugin.CodeGeneratorResponse for _, fname := range req.FileToGenerate { f := files[fname] for _, service := range f.GetService() { content := makeContent(service.GetName(), service.GetMethod()) // 出力 内容 res.File = append(res.File, &plugin.CodeGeneratorResponse_File{ // 出力 内容 入 Name: proto.String("friends.txt"), Content: proto.String(content), }) } } return &res } // 生成 内容 作 func makeContent(serviceName string, methods []*descriptor.MethodDescriptorProto) string { var content string for _, m := range methods { methodName := m.GetName() content += fmt.Sprintf(" %s %s 得意 \n", serviceName, methodName) } return content }
  23. 2019/4/18 reveal.js localhost:8000/?print-pdf 30/34 出力したい内容を標準出力する 出力したい内容を標準出力する import ( "os" "github.com/golang/protobuf/proto"

    plugin "github.com/golang/protobuf/protoc-gen-go/plugin" ) // plugin.CodeGeneratorResponse 列 標準出力 func output(res *plugin.CodeGeneratorResponse) error { buf, err := proto.Marshal(res) if err != nil { return err } _, err = os.Stdout.Write(buf) return err } ...
  24. 2019/4/18 reveal.js localhost:8000/?print-pdf 31/34 main main func main() { req,

    err := parse(os.Stdin) if err != nil { panic(err) } res := process(req) err = output(res) if err != nil { panic(err) } }
  25. 2019/4/18 reveal.js localhost:8000/?print-pdf 32/34 の実行 の実行 ビルドしてパスの通った場所に置く プラグイン実行! 生成されたファイルを確認してみる 超簡単!!!

    $ go build -o protoc-gen-friends plugin.go $ mv protoc-gen-friends $GOPATH/bin/ $ protoc --friends_out=. sample.proto $ cat friends.txt Sample Hello 得意
  26. 2019/4/18 reveal.js localhost:8000/?print-pdf 33/34 まとめ まとめ gRPC 良いですね IDL+ コード生成できるし色々便利

    今回は話してませんが、マイクロサービス間で連携しやすいとか双方 向通信が簡単とか他にも色々メリットがある 便利なprotoc プラグインがたくさんある protoc プラグインの実装は簡単 gRPC のシナリオテスト用コードを生成するプラグインを 簡単に作ってみました gRPC が得意なフレンズを目指していきましょう gRPC が得意なフレンズを目指していきましょう https://github.com/yoshd/protoc-gen-stest