概要
JSON のようなものですが、より小さく、より高速で、ネイティブな言語バインディングを生成します。データをどのように構造化するかを一度定義すれば、特別に生成されたソースコードを使用して、さまざまなデータストリームに対して、さまざまな言語で構造化データを簡単に書き込み、読み取りできます。
Protocol Buffers は、定義言語(.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
ファイルに対して呼び出され、対応するプロトコルバッファを操作するためのさまざまなプログラミング言語(このトピックの後半のクロス言語互換性で説明)のコードを生成します。生成された各クラスには、各フィールドへの簡単なアクセサーと、構造全体を生バイトにシリアル化および解析するためのメソッドが含まれています。以下に、これらの生成されたメソッドを使用する例を示します。
Person john = Person.newBuilder()
.setId(1234)
.setName("John Doe")
.setEmail("jdoe@example.com")
.build();
output = new FileOutputStream(args[0]);
john.writeTo(output);
プロトコルバッファはGoogleのあらゆる種類のサービスで広く使用されており、内部のデータはしばらくの間存続する可能性があるため、下位互換性を維持することが重要です。プロトコルバッファは、既存のサービスを破壊することなく、新しいフィールドの追加や既存のフィールドの削除など、プロトコルバッファへの変更をシームレスにサポートします。このトピックの詳細については、このトピックの後半のコードを更新せずにプロト定義を更新するを参照してください。
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内で広く使用されているプロト定義のいくつかの例は、timestamp.proto
とstatus.proto
です。
コードを更新せずに Proto 定義を更新する
ソフトウェア製品が後方互換性を持つのは標準ですが、前方互換性を持つことはあまり一般的ではありません。.proto
定義を更新する際にいくつかの簡単な慣行に従う限り、古いコードは新しく追加されたフィールドを無視して、新しいメッセージを問題なく読み取ります。古いコードにとって、削除されたフィールドはデフォルト値になり、削除された繰り返しフィールドは空になります。「繰り返し」フィールドが何であるかについては、このトピックの後半の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 を使ってデータを操作する方法を示しています。
図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);
その後、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ライブラリの内部メッセージスキーマは、カスタムの用途固有のオプションの拡張機能を許可します。
利用可能なオプションの詳細については、proto2、proto3、またはエディション2023の言語ガイドを参照してください。
カーディナリティとデータ型を設定したら、フィールドの名前を選択します。フィールド名を設定する際に留意すべき点がいくつかあります。
- 一度本番環境で使用されたフィールド名を変更することは、困難な場合があり、不可能であることさえあります。
- フィールド名にはダッシュを含めることはできません。フィールド名の構文の詳細については、メッセージとフィールドの名前を参照してください。
- 繰り返しフィールドには複数形名を使用してください。
フィールドに名前を割り当てた後、フィールド番号を割り当てます。フィールド番号は、再利用したり、目的外に使用したりすることはできません。フィールドを削除する場合は、誰かが誤ってその番号を再利用するのを防ぐために、そのフィールド番号を予約する必要があります。
追加のデータ型サポート
Protocol Buffers は、可変長エンコーディングと固定サイズの両方を使用する整数を含む、多くのスカラー値型をサポートしています。また、それ自体がフィールドに割り当てることができるデータ型であるメッセージを定義することで、独自の複合データ型を作成することもできます。単純な値型と複合値型に加えて、いくつかの一般的な型が公開されています。
履歴
Protocol Buffers プロジェクトの歴史については、Protocol Buffers の歴史を参照してください。
Protocol Buffers オープンソース哲学
Protocol Buffers は2008年にオープンソース化され、Google社外の開発者にも社内で得ているのと同じメリットを提供する方法となりました。私たちは、社内要件をサポートするために変更を行う際に、言語の定期的な更新を通じてオープンソースコミュニティをサポートしています。外部の開発者からの選択されたプルリクエストは受け付けていますが、Googleの特定のニーズに合致しない機能リクエストやバグ修正に常に優先順位を付けることはできません。
開発者コミュニティ
Protocol Buffers の今後の変更を通知し、protobuf の開発者やユーザーとつながるには、Google グループに参加してください。