Protocol Buffers(protobuf)を実際にサービスで使ってみた

protobuf kv エンジニアの皆様こんにちは! ここ数年Web業界ではマイクロサービスのような、 サービスを分けてAPIでやりとりする考えが浸透してきましたね! 弊社でもDBリソースを処理するサービスとWebサービスを分け APIで必要な情報を取得するようなデザインにシフトしてきました。 今回、弊社ではサーバ間通信APIを実装するにあたり、 IDLとしてProtocol Buffers(protobuf)を採用しました。 今日は通信でよく採用されるSwaggerではなくprotobufを使うに至った経緯と、 実際に運用してみた弊社のエンジニアの生の声をお伝えできたらと思います!

Swagger? protobuf?何が違って何がいい?

1個のサービスをチームで開発をしていると、個々の成果物を合体させる為にプラットフォームないしプロトコルなどのインターフェイスが必要になります。また、会社が大きくなってくると、複数のサービスのデータを利用したプロジェクトが発足されたりしてデータの連携が必要になったりします! JSON API(Swagger)はそれを解決するのに人気な選択です。私たちは血と汗と涙をSwagger API 2.0のメンテに努めてきました。しかし、Swagger 3.0( = OpenAPI specifications v3.0 = OASv3.0)が発表されると、リプレイスするには既存のコードを全て書き直さなければいけなくなりました。 OpenAPIの仕様は、SwaggerのAPIの仕様よりは優れています。 例えば、同じ仕様で複数のエンドポイントを定義できるようになりました。 また、スキーマの再利用性を重視し、継承やポリモーフィズムなどの優れた機能を備えています。 ですが、Swagger API 2.0 からOASv3.0にアップグレードするのはとーっても面倒です!苦痛です! 互換性がなくなったため、各言語のSwagger API 2.0で機能していたツールとライブラリのほとんどはOASv3.0では動かなくなりました。また、 OASv3.0には非常に多くの新しい情報が詰め込まれた為、各言語のライブラリおよびツールをメンテナンスをするのは多大な労力を要します。いざ利用しようと思っても、OpenAPIのドキュメントを掘り下げるのは悪夢であり、学習曲線がぐんっと上がります。 ですので、私たちは通信のプラットフォームとしてProtocol Buffers(protobuf v3.0)の検討をはじめ、調査しました。 OpenAPI側はv3.0に更新するにあたり、新しいの仕様の整備や追加をしたのに対し、Protocol Buffers側はv2.0からv3.0アップグレードするにあたり、仕様を削減しシンプルになるように努めました。 シンプルにするために外された機能の1つ目は「拡張機能(extensions)」です。これは、サードパーティの拡張機能で使用可能なフィールド番号を予約する構文です。 もう1つは「required」の削除です。 protobuf v3.0では、すべてのフィールドはオプションと見なされます。これにより、すべてのプロパティがnullである可能性があるという仮定により、実装の障害許容性が強化されました。

メリット デメリット
Google Protocol Buffers 3
  • 30分あれば仕様全体を学習できる
  • 型のないスクリプト言語でも基本的な型チェックをすることができる
  • すべての値でNULLを許容するので堅牢な実装ができる
  • シンプルであるため、サーバー間通信に適しています
  • JSONシリアル化のオーバーヘッドを排除することで、全体的なパフォーマンスが向上します
  • 各プログラミング言語のアーティファクトファイルを生成する必要がある
  • 継承とポリモーフィズムの欠如(これは良いことです)
Swagger
  • サーバとクライアント通信の表現が得意
  • 継承とポリモーフィズムが利用できる
  • 沢山のコミュニティやユーザがサポートしているので情報が豊富
  • 急な学習曲線
  • 機能が肥大化しているためメンテナンスが困難
  • コードをジェネレートするツールのライブラリがまだ存在していない

上の図から総合的に判断し、私達は社内の通信プラットフォームにprotobufを採用することにしました。

参考

https://developers.google.com/protocol-buffers

併用して利用した技術について

Prototool

protobufはスキーマ言語です。利用している言語にコンパイルする必要があります。コンパイルはエンジニアにとって常に面倒な事です。そんな開発を迅速に進めるためにUber社の開発した Prototool を使用しました。

Prototool とは?

Prototoolは以下の事をコマンドから簡単に実行できるようにするツールです。 ・新規`proto`ファイルの生成(create) ・`proto`ファイルに記述ミスがないかのチェック(lint) ・`proto`ファイルから各言語(PHPやRuby等)のファイルを生成(geneate) など、protobufを使う為に必要なコマンドが詰まっています! インストール方法はbrewやcurlでの導入の他、Dockerに内包されたものもあります。

参考

https://github.com/uber/prototool/blob/dev/docs/install.md
https://github.com/uber/prototool

gRPC

サーバー間の通信をするのに、gRPC(Google Remote Procedure Calls)とprotobufは相性がいいです!(gRPCはそもそもprotobufを利用している) protobufオブジェクトの効率的なシリアライズにより、gRPCサービスから超高速応答を実現しました。 protobufのもう1つの歓迎すべき利点は、ドキュメントが非常に読みやすく簡潔であることです。これにより、開発チーム間の通信コストが大幅に削減されました。

参考

https://grpc.io/
https://qiita.com/oohira/items/63b5ccb2bf1a913659d6

サーバサイドエンジニアとクライアントサイドエンジニアの実用してみての所感

サーバサイドエンジニアの実用

プロジェクトでgRPCを使うに伴い、protobufを使う機会がありました。 今回の開発は以下の流れで行いました。

開発の流れ

1. protobufファイルの作成

prototoolのコマンドを使ってprotobufのmodelとapiのファイルを作成します。 以下の例ではarticle.protoというmodelファイルと、article_api.protoというAPIファイルを生成します。

prototool create model/article.proto
prototool create article_api.proto
modelとapiを書く

作成したmodelとapiのファイルに対してprotobufのコードを書きます。

3. lintチェック

以下のPrototoolのコマンドでlintチェックをしてprotobufのコードに間違いが無いかチェックします。

prototool lint
4. gRPCファイルの生成

以下のPrototoolのコマンドを実行するとPrototoolで設定した言語のgrpcファイルが生成されます。

prototool generate
5.テスト

今回はrequestのパラメーターに対してだけtestコードを書きます。 そしてそのテストが通るかどうかをテストします。 その後データの不備が無いかどうかをBloomRPCというgRPC版Post Manみたいなアプリケーションを使って、問題なく取得できているか、データに不備は無いかなどをチェックします。

感想

今回始めてprotobufを使ったのですが、とてもメリットが多いと感じました。 学習の方法としては、ドキュメントを読んで、既存のコードをサラッと読むだけで使い方はすぐに分かるくらいシンプルな作りでした。 Protocol BuffersはAPI仕様書の代わりになり、API仕様書と実際のAPIでREQUESTパラメーターやGETパラメーターに差異が出ることもなくなるのでとてもいいです。 悪かった点としてはデバックがしにくいくらいでした。 これはProtocol Buffersのせいではないのですが、デバックのときに「何行目が間違えています」みたいなエラーが出てくれないのでエラー箇所を探すのに少し苦労しました。 しかしエラーの内容は教えてくれるので、そこを修正すれば、grpcで接続するときにエラーが起こることは無いので、swagerと比べるとだいぶましです。 また今回はPrototoolの恩恵も大いに受けた気がします。もしPrototoolを使わなかったら初期設定にもう少し苦労したと思います。

クライアントサイドのエンジニア

「Protocol Buffers API」と連携したサービスの開発にあたり、クライアントサイドでは「.proto」ファイルをAPI仕様書として参照していました。その中で、便利だと思ったシーンを3点ご紹介いたします。

1. 「.proto」ファイルを現行版のAPI仕様書として信頼できる

これまで、APIと連携する開発を進める際は、まずAPI仕様書の調査から始めていました。パラメータを確認し、テストリクエストを送信、レスポンスを調べ、という工程を何度も繰り返し、必要なデータを得られるかどうかをチェックしていきます。  このとき妨げとなるのが、「API仕様書に現行版の仕様が記載されていないこと」「API仕様書に記載されたパラメータ仕様の不確かさ」であると感じていました。  APIは機能追加や改善により、度々バージョンアップが行われます。一方で、API仕様書の方はメンテナンスされないことも多く、数世代前のバージョンが説明されていることもありました。そのため、使いたかったパラメータが削除されていたり、より便利なパラメータが追加されていたりということを、第三者サイトで知るというケースを経験しました。  また、API仕様書にはリクエストに使用できるパラメータ名称や値の仕様が記載されています。数値や文字列、配列など、リクエスト例とともに案内されています。しかし、API仕様書自体にはフォーマットがなく、記載の充実度は一定ではありません。桁数、最大値・最小値といったパラメータの仕様を、テストリクエストによって調べなければならないときもありました。  しかし、「.proto」ファイルをAPI仕様書として参照することで、前述の作業はすべて不要となりました。  「.proto」ファイルにはAPIのIFが定義されていて、パラメータの増減、レスポンスの変更など、現行板の実装がすべて反映されます。現行版のAPI仕様と乖離することがありません。型や桁がシンプルかつ詳細に定義されているため、煩雑なテストリクエストを送信する必要もなくなりました。

2. 作業中に参照するべきウィンドウ(ファイル)がシンプル

APIのテストリクエスト中は、次のようなウィンドウ(ファイル)を開くのが常でした。 ・API仕様書(pdfファイル) ・テストリクエスト結果のメモ(excelファイル) ・テストリクエストコードを記述するエディタ ・レスポンスを表示するブラウザ  こうしたウィンドウを行き来し、開発を進めていました。  しかし、「.proto」ファイルをAPI仕様書とすると、シンプルになりました。 ・「.proto」ファイル(ブラウザやエディタ) ・レスポンスを表示するブラウザ ・テストリクエストコードを記述するエディタ  さらに、「BloomRPC」を利用すると、ウィンドウはたった1つで完結します。

3. コミュニケーションが確実になり短縮される

クライアントサイドの開発とAPIの開発が並行していて、クライアントサイドで必要なパラメータやデータの追加を依頼する場面がありました。この依頼のステータス、また新たに追加していただいたIFの仕様も、「.proto」ファイルを参照するだけです。前述の通り、シンプルかつ詳細なフォーマットにより、齟齬なく意思疎通が可能でした。

まとめ

通信のプラットフォームは用途に合わせたものの選択をするべきです。 今回弊社ではサーバ間通信として、JsonAPIではなくGoogle Protocol Buffersを利用しましたがこれは正しい選択でした! 記述量もコードも見やすい為メンテナンスがしやすいのでオススメです! protobufをサーバ&クライアント間の通信としてはまだ利用してませんが、 シリアライゼーションの部分を解決できるのならアリな選択かもしれません。

Techブログ 新着記事一覧