概要

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 を操作するための様々なプログラミング言語のコードを生成します(このトピックの後半にあるクロス言語互換性で説明します)。生成された各クラスには、各フィールドのシンプルなアクセサと、構造全体をバイト列との間でシリアライズおよびパースするメソッドが含まれています。以下に、これらの生成されたメソッドを使用した例を示します

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 ファイルにアクセスすることなく、メッセージを完全に解釈することはできません。
  • 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 ファイルを定義する際、カーディナリティ(単数または繰り返し)を指定できます。proto2 と proto3 では、フィールドがオプションであるかどうかも指定できます。proto3 では、フィールドをオプションに設定すると、暗黙的な存在から明示的な存在に変更されます

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

フィールドは以下の型にすることもできます。

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

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

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

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

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

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

追加のデータ型サポート

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

履歴

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

Protocol Buffers のオープンソース哲学

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

開発者コミュニティ

Protocol Buffers の今後の変更について通知を受け取ったり、protobuf の開発者やユーザーとつながるには、Google グループに参加してください

その他のリソース