Protocol Buffers (Protobuf) は構造化データのためのバイナリシリアライゼーション形式であり、Google がサービス間の効率的な情報交換のために開発しました。この形式では .proto ファイルでスキーマを事前に定義する必要があり、そこから様々な言語のコードが生成されます。Google の公式ドキュメントによると、Protobuf は JSON より 3-10 倍小さなメッセージサイズを実現します。Protobuf は gRPC、Google Maps および数千の内部サービスで使用されています。
ポイント
Protocol Buffers は Google が開発した構造化データのシリアライゼーションメカニズムで、JSON や XML と似ていますが、根本的な違いがあります:データはバイナリ形式でエンコードされます。つまり、Protobuf メッセージは肉眼で読むことはできませんが、少ないスペースでテキストベースのものより高速に処理できます。
Protobuf は、サービス間のデータ交換におけるパフォーマンス問題を解決するために Google 内部で作られました。2008年にこの技術はオープンソースプロジェクトとなり、C++、Java、Python、Go、JavaScript、Kotlin、Swift、Dart など多くの言語をサポートしています。2016年にリリースされた proto3 バージョンは構文を簡素化し、さらに多くの言語のサポートを追加しました。
Protobuf と JSON の主な違いは、データ交換を開始する前にスキーマを定義する必要があることです。スキーマファイル (.proto) はメッセージ構造を記述します:フィールド、型、および固有のフィールド番号。このスキーマから、protoc コンパイラがシリアライゼーションとデシリアライゼーションのためのターゲット言語のクラスを生成します。
Protobuf のアーキテクチャは、スキーマ定義言語 (.proto)、protoc コンパイラ、および特定の言語用のランタイムライブラリの 3 つの主要コンポーネントで構成されます。開発者は .proto ファイルでデータ構造を記述し、コンパイルを実行し、このデータを操作するための完成したクラスを得られます。
Protobuf メッセージの各フィールドには固有の番号(フィールド番号)があります。これは順列番号ではなく、バイナリ形式でフィールドを識別するためのタグです。1~15 の番号は 1 バイトで、16~2047 は 2 バイトでエンコードされます。そのため、使用頻度の高い重要なフィールドはサイズ最適化のために 1~15 の番号を付けるべきです。
Protobuf は、スカラー (int32, int64, float, double, bool, string, bytes)、統括 (enum)、複合 (message)、および特殊 (oneof, map) など幅広いタイプをサポートしています。各タイプには、対応する使用ケースに最適化された特定のバイナリ表現があります。
| .proto 型 | C++ 型 | Java/Kotlin | 説明 |
|---|---|---|---|
| double | double | double | 64ビット浮動小数点数 |
| float | float | float | 32ビット浮動小数点数 |
| int32 | int32 | int | 32ビット、可変長エンコーディング |
| int64 | int64 | long | 64ビット、可変長エンコーディング |
| string | string | String | UTF-8 文字列 |
| bytes | string | ByteString | 任意のバイト列 |
| bool | bool | boolean | true / false |
Protobuf は、テキストベースの形式に比べて、メッセージサイズ、シリアライゼーション速度、および懈密な型付けの 3 つの主要な利点を提供します。高負荷システムやトラフィックの制限されたモバイルアプリでは、これらの利点が重要になります。
Protobuf のバイナリ表現は、数値に可変長エンコーディング (Varint) を使用します:小さな数値は 1 バイト、大きな数値は最大 10 バイトです。これにより、JSON や XML でテキスト文字列として数十バイトを占める識別子フラグカウンタを効率的にエンコードできます。例えば、150 という数値は Protobuf で 2 バイト、JSON で 3 バイト (“150” というテキスト)、XML で 3 バイト + タグを占めます。
もう一つの利点は下位互換性です。スキーマに新しいフィールドを追加しても、古いクライアントは破壊されません:不明なフィールドを無視するだけです。フィールドを削除するには、将来の競合を避けるためにその番号を予約するだけでよいです。
.proto スキーマは、専用言語でデータ構造を記述します。ファイルは、構文(proto3)、パッケージ、およびインポート依存関係の指定から始まります。各メッセージは、フィールドを伴うキーワード message で定義され、各フィールドには型、名前、固有番号があります。
良いスキーマの実践:頻繁に使用されるフィールドには 1~15 のフィールド番号、意味のある名前、関連するフィールドを別の message にまとめること、複数のオプションのうちの 1 つしか持てないフィールドに oneof を使用すること。
syntax = "proto3";
package mobileapp;
message User {
string user_id = 1;
string name = 2;
string email = 3;
int32 age = 4;
UserRole role = 5;
repeated string tags = 6;
map<string, string> metadata = 7;
}
enum UserRole {
USER_ROLE_UNSPECIFIED = 0;
USER_ROLE_USER = 1;
USER_ROLE_ADMIN = 2;
USER_ROLE_MODERATOR = 3;
}
メッセージはネスト深げにできます:Profile 型のprofileフィールドは内部にユーザーデータを保持します。ネスト深げのサポートにより、定義を複製せずに複雑な階層構造を記述できます。
syntax = "proto3";
package mobileapp;
message Order {
string order_id = 1;
repeated OrderItem items = 2;
double total_price = 3;
PaymentInfo payment = 4;
}
message OrderItem {
string product_id = 1;
string title = 2;
int32 quantity = 3;
double price = 4;
}
message PaymentInfo {
string method = 1;
string transaction_id = 2;
double amount = 3;
}
protoc コンパイラは .proto ファイルをターゲット言語のソースコードに変換します。Kotlin/Java では --java_out パラメータが使用され、Swift では --swift_out(Apple Swift Protobuf プラグインを通じて)、Dart では --dart_out が使用されます。生成されたクラスには、メッセージ構築のためのビルダーメソッドとシリアライゼーション/デシリアライゼーションメソッドが含まれています。
Android で Protobuf を使用するには、Gradle で com.google.protobuf プラグインバージョン 0.9+ を使用します。プラグインを追加し、.proto ファイルを指定すると、ビルド自動的にアプリで使用できる Kotlin クラスを生成します。
import com.google.protobuf.kotlin.toByteString
import com.example.mobileapp.UserOuterClass.User
fun createUser(): User {
return User.newBuilder()
.setUserId("usr_001")
.setName("IT Sectr")
.setEmail("team@itsectr.com")
.setAge(5)
.setRole(UserOuterClass.UserRole.USER_ROLE_ADMIN)
.addTags("mobile")
.addTags("backend")
.build()
}
fun serializeAndDeserialize(user: User): User {
// バイナリ形式へのシリアライゼーション
val bytes = user.toByteArray()
// バイナリ形式からのデシリアライゼーション
return User.parseFrom(bytes)
}
Protobuf は、マイクロサービスアーキテクチャやモバイルアプリで特に効果的です。よくあるシナリオを考えてみましょう:モバイルアプリが gRPC を通じてサーバーから商品リストを受け取ります。Protobuf メッセージには、識別子、名前、価格、カテゴリなどの商品情報が含まれています。バイナリ形式は JSON と比較してレスポンスサイズを 5-8 倍縮小します。
サーバーサイドの Protobuf を使用した gRPC サービスの例。サービスは GetProducts RPC メソッドを宣言し、ページネーションパラメータを含むリクエストを受け入れ、商品リストを返します。Kotlin の実装は、リクエストとレスポンスを操作するために生成されたクラスを使用します。
import com.example.mobileapp.ProductServiceGrpcKt
import com.example.mobileapp.ProductOuterClass.Product
import com.example.mobileapp.ProductOuterClass.GetProductsRequest
import com.example.mobileapp.ProductOuterClass.GetProductsResponse
class ProductService : ProductServiceGrpcKt.ProductServiceCoroutineImplBase() {
override suspend fun getProducts(
request: GetProductsRequest
): GetProductsResponse {
val products = fetchProductsFromDb(
page = request.page,
limit = request.limit
)
return GetProductsResponse.newBuilder()
.addAllProducts(products)
.setTotalCount(products.size)
.build()
}
}
Android プロジェクトでの Protobuf 設定は、com.google.protobuf Gradle プラグインを通じて行います。このプラグインは、ビルド中に自動的に protoc コンパイラを実行し、.proto ファイルから Kotlin クラスを生成します。使用するには、プロジェクトレベルの build.gradle にプラグインを追加し、アプリモジュールに適用する必要があります。
Gradle を設定した後、.proto ファイルを src/main/proto ディレクトリに置きます。protoc コンパイラは各ビルドでこれらを処理し、アプリコードで使用できる Kotlin クラスを生成します。他のプロジェクトライブラリとの依存関係競合を避けるために、protobuf および protoc のバージョンを正しく指定することが重要です。
iOS プロジェクトでは、CocoaPods または Swift Package Manager を通じて Protobuf を接続します。Swift Protobuf プラグインは、Codable プロトコルに準拠した Swift 構造体を自動生成します。Flutter の Dart プロジェクトでは、protobuf パッケージが使用され、コンパイルは dart run protoc_plugin で行います。
よくある質問
Protobuf は必須スキーマを持つバイナリ形式で、JSON より 3-10 倍コンパクトです。JSON はテキストベースで人間が読め、スキーマが不要です。Protobuf は高速にシリアライズ・デシリアライズできますが、.proto ファイルのコンパイルが必要です。JSON はデバッグが容易で事前設定が不要です。
ご利用のプラットフォームに応じた protobuf の GitHub リリースからprotocをダウンロードしてください。macOS の場合は、brew install protobuf でインストールできます。Windows の場合は zip アーカイブをダウンロードし、protoc.exe を PATH に追加します。Android/Kotlin の場合は、com.google.protobuf Gradle プラグインを使用します。これはビルド中に自動的にコンパイルを実行します。
gRPC は Google による高パフォーマンス RPC フレームワークで、インターフェース定義言語 (IDL) およびシリアライゼーション形式として Protobuf を使用します。gRPC は .proto ファイルでサービスと RPC メソッドを定義し、クライアントとサーバーのコードを生成し、ストリーミングおよびバイナリプロトコルをサポートします。
Google が公式にサポート: C++、Java、Kotlin、Python、Go、Ruby、C#、PHP、JavaScript、Objective-C、Swift、Dart。コミュニティは Rust、TypeScript、Scala、Lua などの言語のサポートを開発しています。モバイル開発には、Kotlin/Java(Android)および Swift/Objective-C(iOS)が利用可能です。
Protobuf は次のルールで下位互換性をサポートしています:フィールド番号を変更しない、フィールドを削除しない (reserved を使用)、新しい番号で新しいフィールドを追加する。古いクライアントは不明なフィールドを無視し、新しいクライアントは不充の古いフィールドにデフォルト値を取得します。
まとめ
ターンキー方式のモバイルアプリケーションを開発します
IT Sectrは2017年からスタートアップや企業向けにiOS・Androidアプリケーションを開発しています。私たちがご相談に乗り、最適なソリューションをご提案します。