概要

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

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

プロトコルバッファは、定義言語(`.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` ファイルに対して呼び出され、対応するプロトコルバッファを操作するための様々なプログラミング言語のコードを生成します (このトピックの後半の「クロス言語互換性」で説明)。生成された各クラスには、各フィールドの単純なアクセサーと、構造全体を 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 は、既存のサービスを破壊することなく、新しいフィールドの追加や既存フィールドの削除を含む、あらゆるプロトコルバッファの変更をシームレスにサポートします。このトピックの詳細については、このトピックの後半にある「コードを更新せずに Proto 定義を更新する」を参照してください。

Protocol Buffers を使用するメリットは何ですか?

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

プロトコルバッファを使用する利点には、次のようなものがあります。

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

クロス言語互換性

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

次の言語は、Protocol Buffers コンパイラである protoc で直接サポートされています。

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

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

クロスプロジェクトサポート

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

Google内で広く使用されているプロト定義の例として、timestamp.protostatus.protoがあります。

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

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

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

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

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

  • Protocol Buffersは、通常、メッセージ全体を一度にメモリに読み込むことができ、オブジェクトグラフよりも大きくないことを前提としています。数メガバイトを超えるデータの場合は、別のソリューションを検討してください。より大きなデータを扱う場合、シリアライズされたコピーのためにデータが効果的に複数コピーされることになり、メモリ使用量が驚くほど急増する可能性があります。
  • Protocol Buffersがシリアライズされると、同じデータでも多くの異なるバイナリシリアライゼーションを持つことができます。2つのメッセージを完全にパースせずに等価性を比較することはできません。
  • メッセージは圧縮されません。メッセージは他のファイルと同様にzipまたはgzipで圧縮できますが、JPEGやPNGで使用されるような特殊な圧縮アルゴリズムは、適切なタイプのデータに対してはるかに小さいファイルを生成します。
  • Protocol Buffer のメッセージは、浮動小数点数の大規模な多次元配列を伴う多くの科学技術用途において、サイズと速度の両方で最大限に効率的ではありません。これらのアプリケーションでは、FITS や同様の形式の方がオーバーヘッドが少なくなります。
  • Protocol Buffers は、Fortran や IDL など、科学計算で人気のある非オブジェクト指向言語ではあまりサポートされていません。
  • Protocol Buffers のメッセージは、本質的にデータを自己記述しませんが、自己記述を実装するために使用できる完全なリフレクティブスキーマを持っています。つまり、対応する `.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 のワークフロー

プロトコルバッファによって生成されたコードは、ファイルやストリームからデータを取得したり、データから個々の値を抽出したり、データが存在するかどうかを確認したり、データをファイルやストリームにシリアル化し直したりするなどのユーティリティメソッドを提供します。

以下のコードサンプルは、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);

その後、C++のような他の言語でProtocol Buffersが作成するメソッドを使用してデータをデシリアライズできます。

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の言語ガイドを参照してください。

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

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

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

追加のデータ型サポート

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

履歴

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

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

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

開発者コミュニティ

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

追加リソース