8.

Protocol Buffers (.proto) | Google Protobuf / gRPC スキーマ解説

編集
この記事の要点
  • Protocol Buffers (.proto) は Google 製の言語非依存スキーマ定義言語 (IDL)
  • .proto ファイルにメッセージ構造を書き、protoc で各言語のシリアライザをコード生成
  • バイナリは極めてコンパクト(field tag + wire type + payload)、JSON の数倍小さい
  • gRPC の標準スキーマ言語。proto3 が現代の主流
  • 後方互換性ルール: field tag は変えない / 必須は使わない / 列挙は廃止せず予約

要点

  • Protocol Buffers(プロトコル バッファ、略称 Protobuf)は Google が 2008 年に OSS 化した、言語非依存・プラットフォーム非依存のシリアライゼーション形式。スキーマ定義ファイルの拡張子が .proto
  • IDL(Interface Definition Language)でスキーマを書き、protoc コンパイラで各言語のコード(C++/Java/Go/Python/Ruby/C# 等)を自動生成して使う。
  • バイナリエンコードは小さくて高速で、JSON と比べてサイズは 1/3〜1/10、シリアライズ/パースは数倍〜数十倍速い。
  • gRPC のデフォルトのワイヤフォーマットで、マイクロサービス間通信のデファクト基盤。

概要

Protocol Buffers は Google 社内で 2001 年頃から使われていた内部技術が、2008 年に proto2 仕様として公開された。社内インデックスサーバ間の通信形式が起源で、「人間が読むためでなく、機械が大量に処理することに特化したフォーマット」という設計思想を持つ。2016 年に proto3 が公開され、現在の業界利用はほぼ proto3 ベースである。

使い方の流れは独特で、まず .proto ファイルにメッセージ定義を書き、protoc コマンドで言語ごとのクラスを生成する。アプリ側ではそのクラスのインスタンスを作って SerializeToString() / ParseFromString() でバイナリ往復する。スキーマがコード(型)として存在するため、型安全で IDE 補完が効き、フィールド名の typo が即コンパイルエラーになる。これは JSON や CSV にはない大きな利点である。

用途として最大なのは gRPC(Google が開発した HTTP/2 ベースの RPC フレームワーク)のメッセージ形式で、現代のマイクロサービス間通信ではほぼ Protobuf+gRPC が選ばれる。次点で各種設定ファイル、ゲームのセーブデータ、IoT のテレメトリ、機械学習モデル定義(TensorFlow の SavedModel など)でも使われる。

内部構造

proto3 のスキーマ例は次のような形になる。

syntax = "proto3";
package example;

message User {
  int64  id    = 1;
  string name  = 2;
  string email = 3;
  repeated string tags = 4;
}

service UserService {
  rpc GetUser(GetUserRequest) returns (User);
}

キモは各フィールド末尾の = 1 のようなタグ番号(field number)で、これがワイヤ上の識別子になる。フィールド名はバイナリには出ず、タグ番号と型のみ。これにより、フィールド名を後から変えてもバイナリ互換は壊れない(タグ番号さえ固定なら)。逆に、タグ番号を一度割り当てたら絶対に再利用してはいけない

バイナリは TLV(Tag-Length-Value)に近い構造で、各フィールドは「タグ番号 + ワイヤ型(Varint/64bit/Length-delimited/32bit)」のヘッダの後に値を持つ。未知のフィールドは無視して読み飛ばすため、後方互換/前方互換のどちらもとりやすい。

整数は Varint(小さい数値ほど短いバイト数で済む)でエンコードされ、たとえば 0〜127 はわずか 1 バイト。これが JSON との容量差の大きな要因になる。

主な用途

  • gRPC のメッセージ: service + rpc 定義から両端のクライアント/サーバスタブを生成。マイクロサービス間 RPC の事実上の標準。
  • Kafka のペイロード: Avro と並ぶ選択肢。Schema Registry が Protobuf も対応している。
  • 機械学習: TensorFlow の SavedModel、ONNX、TFRecord などが Protobuf 形式。
  • ゲーム / モバイル: 通信量・パース時間を切り詰めたい場面で JSON より優位。
  • 各種設定ファイル: Envoy / Istio / Kubernetes の内部表現の一部など。

関連形式との比較

形式スキーマサイズ主な使い所
Protocol Buffers.proto、コード生成非常に小gRPC、内部 RPC
AvroJSON、ファイル同梱Kafka、長期保管
JSON無し(or JSON Schema)Web API、ログ
Thrift.thrift、コード生成Facebook 系、古めの RPC
FlatBuffers.fbs、コード生成小(zero-copy)ゲーム、リアルタイム

Avro との対比でいうと、Protobuf は「アプリのコードに型としてスキーマを焼き込む」設計、Avro は「データに同梱して動的に解釈する」設計で、思想がほぼ正反対である。Web API の感覚で扱うなら Protobuf、データ基盤として保管するなら Avro、というのが大まかな指針になる。

コマンド・ツール

公式コンパイラ protoc をインストールして使う。

$ protoc --version
libprotoc 3.21.12

# Python 用コード生成
$ protoc --python_out=. user.proto

# Go 用
$ protoc --go_out=. --go-grpc_out=. user.proto

Python での読み書き例。

from user_pb2 import User

u = User(id=1, name="Alice", email="a@example.com", tags=["admin", "ops"])
buf = u.SerializeToString()       # バイナリ化
print(len(buf))                    # 数十バイト

u2 = User()
u2.ParseFromString(buf)            # 復号

デバッグ用にバイナリと JSON を往復する CLI として protoc --decodebufgrpcurl がよく使われる。buf は Lint・Breaking Change 検出を備えた現代的なツールチェーンで、スキーマ運用に強く推奨される。

注意点

  • タグ番号は不可逆: フィールドを削除しても、その番号は reserved にして二度と使わない運用が必須。番号再利用は互換性破壊の温床。
  • 必須フィールドが無い: proto3 ではすべてオプショナル。欠落と「ゼロ値(0/空文字)」が区別できない問題があり、明示的に区別したいなら optional 修飾子(proto3.15 以降)か Wrapper 型を使う。
  • 人間が読めない: バイナリ前提のため、ログ用途には不向き。protoc --decode で覗くか、JSON マッピング(gRPC-Gateway 等)を併用する。
  • スキーマ管理が運用要: アプリ間でスキーマがずれるとパースエラーや silent な値破損が起きる。リポジトリ集中管理+ Breaking Change チェック(buf breaking)の運用がほぼ必須。

関連リンク

編集
Post Share
子ページ

子ページはありません

同階層のページ
  1. SQL ダンプ(.sql)
  2. SQLite(.sqlite / .sqlite3 / .db)
  3. Parquet(.parquet)
  4. Avro(.avro)
  5. ORC(.orc)
  6. NDJSON / JSONL(.ndjson / .jsonl)
  7. BSON(.bson)
  8. Protocol Buffers(.proto)
  9. Feather / Arrow IPC(.feather / .ipc / .arrow)
  10. DB ダンプ(.dump / .bak)

最近更新/作成されたページ