概要

Protocol Buffers は、構造化データをシリアライズするための、言語中立、プラットフォーム中立、拡張可能なメカニズムです。

JSON に似ていますが、より小さく高速で、ネイティブ言語バインディングを生成します。データの構造を一度定義すれば、特別な生成されたソースコードを使用して、さまざまなデータストリームとの間で、またさまざまな言語を使用して、構造化データを簡単に書き込みおよび読み取りできます。

Protocol buffers は、定義言語 (.proto ファイルで作成)、データとインターフェースするために proto コンパイラーが生成するコード、言語固有のランタイムライブラリ、ファイルに書き込まれる (またはネットワーク接続を介して送信される) データのシリアライゼーション形式、およびシリアライズされたデータの組み合わせです。

Protocol Buffers はどのような問題を解決しますか?

Protocol buffers は、数メガバイトまでのサイズの型付き構造化データのパケットのシリアライゼーション形式を提供します。この形式は、一時的なネットワークトラフィックと長期データストレージの両方に適しています。Protocol buffers は、既存のデータを無効にしたり、コードを更新したりすることなく、新しい情報で拡張できます。

Protocol buffers は、Google で最も一般的に使用されているデータ形式です。サーバー間通信やディスク上のデータのアーカイブストレージに広く使用されています。Protocol buffer のメッセージサービスは、エンジニアが作成した .proto ファイルによって記述されます。以下に message の例を示します

edition = "2023";

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

proto コンパイラーは、ビルド時に .proto ファイル上で呼び出され、対応する protocol buffer を操作するために、さまざまなプログラミング言語 (このトピックの後半の 言語間の互換性 で説明) でコードを生成します。生成された各クラスには、各フィールドへの単純なアクセサーと、構造全体を raw バイトとの間でシリアライズおよびパースするメソッドが含まれています。以下に、生成されたメソッドを使用する例を示します

Person john = Person.newBuilder()
    .setId(1234)
    .setName("John Doe")
    .setEmail("jdoe@example.com")
    .build();
output = new FileOutputStream(args[0]);
john.writeTo(output);

Protocol buffers は Google のあらゆる種類のサービスで広く使用されており、その中のデータはしばらくの間永続化される可能性があるため、下位互換性を維持することが重要です。Protocol buffers を使用すると、既存のサービスを中断することなく、新しいフィールドの追加や既存のフィールドの削除など、protocol buffer への変更をシームレスにサポートできます。このトピックの詳細については、このトピックの後半の コードを更新せずに Proto 定義を更新する を参照してください。

Protocol Buffers を使用する利点は何ですか?

Protocol buffers は、言語中立、プラットフォーム中立、拡張可能な方法で、構造化されたレコードのような型付きデータをシリアライズする必要があるあらゆる状況に最適です。これらは、通信プロトコル (gRPC と共に) およびデータストレージを定義するために最もよく使用されます。

protocol buffers を使用する利点には、次のようなものがあります

  • コンパクトなデータ保存
  • 高速なパース
  • 多くのプログラミング言語で利用可能
  • 自動生成されたクラスによる最適化された機能

言語間の互換性

同じメッセージは、サポートされている任意のプログラミング言語で記述されたコードで読み取ることができます。あるプラットフォーム上の Java プログラムで、あるソフトウェアシステムからデータをキャプチャし、.proto 定義に基づいてシリアライズし、別のプラットフォームで実行されている別の Python アプリケーションで、そのシリアライズされたデータから特定の値を抽出できます。

次の言語は、protocol buffers コンパイラー protoc で直接サポートされています

次の言語は Google によってサポートされていますが、プロジェクトのソースコードは GitHub リポジトリにあります。protoc コンパイラーは、これらの言語のプラグインを使用します

追加の言語は Google によって直接サポートされていませんが、他の GitHub プロジェクトによってサポートされています。これらの言語については、Protocol Buffers のサードパーティ製アドオン で説明しています。

プロジェクト間のサポート

特定のプロジェクトのコードベース外にある .proto ファイルで message 型を定義することにより、プロジェクト間で protocol buffers を使用できます。message 型または enum を定義していて、それがあなたの直接のチーム外で広く使用されると予想される場合は、依存関係のない独自のファイルにそれらを配置できます。

Google 内で広く使用されている proto 定義のいくつかの例は、timestamp.protostatus.proto です。

コードを更新せずに Proto 定義を更新する

ソフトウェア製品が下位互換性を持つことは標準ですが、上位互換性を持つことはあまり一般的ではありません。.proto 定義を更新するときに、いくつかの 簡単なプラクティス に従う限り、古いコードは新しく追加されたフィールドを無視して、新しいメッセージを問題なく読み取ります。古いコードでは、削除されたフィールドはデフォルト値を持ち、削除された repeated フィールドは空になります。「repeated」フィールドの詳細については、このトピックの後半の Protocol Buffers 定義構文 を参照してください。

新しいコードも古いメッセージを透過的に読み取ります。新しいフィールドは古いメッセージには存在しません。このような場合、protocol buffers は適切なデフォルト値を提供します。

Protocol Buffers が適さないのはどのような場合ですか?

Protocol buffers はすべてのデータに適しているわけではありません。特に

  • Protocol buffers は、メッセージ全体を一度にメモリにロードでき、オブジェクトグラフよりも大きくないことを前提とする傾向があります。数メガバイトを超えるデータの場合は、別のソリューションを検討してください。より大きなデータを扱う場合、シリアライズされたコピーにより、データのコピーが複数作成され、メモリ使用量が急増する可能性があります。
  • protocol buffers がシリアライズされると、同じデータに多くの異なるバイナリシリアライゼーションを含めることができます。2 つのメッセージを完全にパースせずに、それらの等価性を比較することはできません。
  • メッセージは圧縮されていません。メッセージは他のファイルと同様に zip または gzip で圧縮できますが、JPEG や PNG で使用されているような特殊用途の圧縮アルゴリズムは、適切なタイプのデータに対してはるかに小さなファイルを生成します。
  • Protocol buffer メッセージは、浮動小数点数の大規模な多次元配列を含む多くの科学およびエンジニアリング用途において、サイズと速度の両方で最大効率を下回ります。これらのアプリケーションでは、FITS および同様の形式の方がオーバーヘッドが少なくなります。
  • Protocol buffers は、Fortran や IDL など、科学計算で一般的な非オブジェクト指向言語では十分にサポートされていません。
  • Protocol buffer メッセージは、本質的にデータを自己記述しませんが、自己記述を実装するために使用できる完全にリフレクティブなスキーマを持っています。つまり、対応する .proto ファイルにアクセスせずに、1 つを完全に解釈することはできません。
  • Protocol buffers は、どの組織の正式な標準でもありません。これにより、標準の上に構築するための法的またはその他の要件がある環境での使用には適していません。

誰が Protocol Buffers を使用していますか?

多くのプロジェクトが protocol buffers を使用しています。以下を含みます

Protocol Buffers はどのように機能しますか?

次の図は、protocol buffers を使用してデータを処理する方法を示しています。

Compilation workflow showing the creation of a proto file, generated code, and compiled classes
図 1. Protocol buffers のワークフロー

protocol buffers によって生成されたコードは、ファイルおよびストリームからデータを取得し、データから個々の値を抽出し、データが存在するかどうかを確認し、データをファイルまたはストリームにシリアライズして戻すためのユーティリティメソッド、およびその他の便利な機能を提供します。

次のコードサンプルは、Java でのこのフローの例を示しています。前述のように、これは .proto 定義です

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

この .proto ファイルをコンパイルすると、次の Java コードのように、新しいインスタンスを作成するために使用できる Builder クラスが作成されます

Person john = Person.newBuilder()
    .setId(1234)
    .setName("John Doe")
    .setEmail("jdoe@example.com")
    .build();
output = new FileOutputStream(args[0]);
john.writeTo(output);

次に、protocol buffers が C++ などの他の言語で作成するメソッドを使用して、データをデシリアライズできます

Person john;
fstream input(argv[1], ios::in | ios::binary);
john.ParseFromIstream(&input);
int id = john.id();
std::string name = john.name();
std::string email = john.email();

Protocol Buffers 定義構文

.proto ファイルを定義するときに、カーディナリティ (単数または repeated) を指定できます。proto2 および proto3 では、フィールドがオプションであるかどうかを指定することもできます。proto3 では、フィールドを optional に設定すると、暗黙的な存在から明示的な存在に変わります

フィールドのカーディナリティを設定した後、データ型を指定します。Protocol buffers は、整数、ブール値、浮動小数点数など、通常のプリミティブデータ型をサポートしています。完全なリストについては、スカラー値型 を参照してください。

フィールドは次の型にもできます

  • message 型。これにより、データの繰り返しセットなど、定義の一部をネストできます。
  • enum 型。これにより、選択する値のセットを指定できます。
  • oneof 型。メッセージに多くのオプションフィールドがあり、同時に設定されるフィールドが最大 1 つの場合に使用できます。
  • map 型。定義にキーと値のペアを追加します。

メッセージは、メッセージ自体の外部にフィールドを定義するために拡張を許可できます。たとえば、protobuf ライブラリの内部メッセージスキーマは、カスタムの、使用法固有のオプションの拡張を許可します。

利用可能なオプションの詳細については、proto2proto3、または edition 2023 の言語ガイドを参照してください。

カーディナリティとデータ型を設定したら、フィールドの名前を選択します。フィールド名を設定する際に留意すべき点がいくつかあります

  • フィールド名が本番環境で使用された後で、フィールド名を変更することは、難しい場合や不可能になる場合さえあります。
  • フィールド名にダッシュを含めることはできません。フィールド名の構文の詳細については、メッセージおよびフィールド名 を参照してください。
  • repeated フィールドには複数形の名前を使用してください。

フィールドに名前を割り当てた後、フィールド番号を割り当てます。フィールド番号は、目的を変更したり、再利用したりすることはできません。フィールドを削除する場合は、誰かが誤って番号を再利用するのを防ぐために、そのフィールド番号を予約する必要があります。

追加のデータ型サポート

Protocol buffers は、可変長エンコーディングと固定サイズの両方を使用する整数を含む、多くのスカラー値型をサポートしています。メッセージ自体がフィールドに割り当てることができるデータ型であるメッセージを定義することにより、独自の複合データ型を作成することもできます。シンプルおよび複合値型に加えて、いくつかの 一般的な型 が公開されています。

歴史

protocol buffers プロジェクトの歴史について読むには、Protocol Buffers の歴史 を参照してください。

Protocol Buffers オープンソースの理念

Protocol buffers は、Google の外部の開発者に、社内で得ているのと同じ利点を提供する方法として、2008 年にオープンソース化されました。私たちは、社内の要件をサポートするために変更を加える際に、言語を定期的に更新することで、オープンソースコミュニティをサポートしています。外部の開発者からの選択されたプルリクエストを受け入れますが、Google の特定のニーズに適合しない機能リクエストとバグ修正を常に優先できるとは限りません。

開発者コミュニティ

Protocol Buffers の今後の変更についてアラートを受け取り、protobuf 開発者およびユーザーとつながるには、Google グループに参加してください

追加リソース