gRPC + Protocol Buffers でバックエンドを開発していると、他サービスや Protocol Buffers プラグインの proto ファイルの管理をしたくなる場合があります。 よくある Protocol Buffers のプラグインではリポジトリを go get してコンパイル時にパスを通すよう指示されますが、どのリビジョンの proto ファイルを使ってスキーマを定義しているのかが不明瞭になります。また、 proto ファイルの場所が散らばるのでコンパイルのコマンドも複雑になります。

本記事では、Protocol Buffers とは何かから、なぜ依存管理が必要か、依存している proto ファイルを管理するにはどうすれば良いかについて述べます。

Protocol Buffersとは

Protocol Buffers (以下protobufと書きます) はデータをシリアライズするためのフォーマットです。同様の用途で使われるものとしては XML や JSON があります。 XML や JSON に比べてデータを小さく、早く扱えることが特徴です。また、シンプルなスキーマ言語を使ってスキーマを定義し、自動生成されるコードを用いて簡単にシリアライズ、デシリアライズを行うことができます。C++, C#, Go, Java, Python など、多くの主要な言語に対応しています。

スキーマの定義は下記のように .proto ファイルに Protocol Buffers の message を記述することによって行えます。

message Person {
  required string name = 1;
  required int32 id = 2;
  string email = 3;
}

参考: Protocol Buffers Developer Guide - Overview

gRPCとは

RPC のフレームワークです。インターフェースに protobuf を使用しており、開発しやすく高性能な点が特徴です。クライアントは別のマシン上のアプリケーションのメソッドを直接呼び出すように API を扱うことができるので、クライアント - サーバやサーバ - サーバで連携するアプリケーションを簡単に開発することができます。メソッドを呼び出すためのコードは各言語向けに自動で生成されるため、インターフェース周りのコードを実装する必要はありません。

RPC のメソッドの定義は下記のように serviceで RPC メソッドを、 message でリクエスト、レスポンスの型を定義することで行なえます。

// The greeter service definition.
service Greeter {
  // Sends a greeting
  rpc SayHello (HelloRequest) returns (HelloReply) {}
}

// The request message containing the user's name.
message HelloRequest {
  string name = 1;
}

// The response message containing the greetings
message HelloReply {
  string message = 1;
}

参考: gRPC Documentation - guide

なぜ依存管理が必要か

protobuf の plugin を使ったり、protobuf でシリアライズする他サービスとの通信を行いたりする場合に外部の proto ファイルを取得する必要があります。よくある Protocol Buffers のプラグインではリポジトリを go get してコンパイル時にパスを通すよう指示されますが、どのリビジョンの proto ファイルを使ってスキーマを定義しているのかが不明瞭になります。また、 proto ファイルの場所が散らばるのでコンパイルのコマンドも複雑になります。

下図は複雑になってしまったコマンドです。依存する proto ファイルの分だけ –proto_path フラグが増えてしまいます。

また、plugin のスキーマや他サービスのスキーマは時間の経過によって変わるので、依存している proto ファイルは管理する必要があります。

protodepとは

protodep は proto ファイルの依存管理ツールです。簡単な toml ファイルを書くだけで、別リポジトリの proto ファイルの依存管理を行うことができます。

2017 年に開発され、最終コミットが 2018 年 2 月と、開発が盛んなわけではないですが、テストが書いてあり特に大きな issue も上がっていないので、完成しているという認識で使っていいかと思います。

protodepを使ってみる

早速使ってみましょう。

インストール

下記コマンドで protodep コマンドをインストールできます。

  • go get github.com/stormcat24/protodep

tomlファイルを書く

protodep では依存しているprotoファイルの管理にtomlファイルを使います。proto ファイルの依存管理をしたいリポジトリの root に protodep.toml を配置する必要があります。

protodep.toml で指定するのは proto ファイルを配置するディレクトリ (proto_outdir) と、依存している proto ファイルを含むディレクトリ (dependencies) です。

protodep.toml は下記のようになります。

proto_outdir = "./proto"

[[dependencies]]
  target = "github.com/stormcat24/protodep/protobuf"
  branch = "master"

[[dependencies]]
  target = "github.com/grpc-ecosystem/grpc-gateway/examples/proto"
  tag = "v1.2.2"
  path = "grpc-gateway/examples/proto"

[[dependencies]]
  target = "github.com/kubernetes/helm/_proto/hapi"
  branch = "master"
  path = "helm/hapi"
  ignores = ["./release", "./rudder", "./services", "./version"]

dependencies では target, revision, branch, ignores を指定することができます。それぞれ次の内容を指定します。

  • target .. protoファイルを含むgitリポジトリを指定する
  • revision .. 対象リビジョン(任意)
  • branch .. 対象ブランチ(任意)
  • path .. protoファイルを配置する、proto_outdirからの相対ディレクトリ(任意)
  • ignores .. protoファイルを取得しないディレクトリ(任意)

protoファイルを取得する

protodep up -f コマンドで protodep.toml の内容に従ってprotoファイルを取得することができます。同時に protodep.lock ファイルが作成(すでに存在していれば更新)されます。

lockファイルには proto ファイルを取得するリポジトリ、リビジョンが記述してあり、 protodep up で、lockファイルの内容に一致するprotoファイルを取得することができます。これで複数開発している場合に使用する proto ファイルを統一することができます。

上記の protodep.tomlで protodep up -f すると下記のように proto ファイルを取得することができます。

以上で proto ファイルの依存管理ができるようになりました。

まとめ

本記事では、Protocol Buffersとは何かから、なぜ依存管理が必要か、依存しているprotoファイルを管理するにはどうすれば良いかについて述べました。protodepを使うことにより、簡単なtomlファイルを書くだけで依存しているprotoファイルの管理ができるようになります。

protodepを使った gRPC + grpc-gateway サーバ(Go)のテンプレートも公開しています。もし興味があれば見てみてください。参考になれば幸いです。

参考