Protocol Buffers(protobuf)はGoogleが開発したデータシリアライズフォーマットです。JSONやXMLに比べてバイナリ形式のため高速・軽量で、gRPCのインターフェイス定義としても使われます。.proto ファイルに型を定義するとコンパイラ(protoc)が各言語のコードを生成します。
JSONとの比較
| 項目 | JSON | Protocol Buffers |
|---|---|---|
| 形式 | テキスト | バイナリ |
| サイズ | 大きい | 3〜10倍小さい |
| 速度 | 普通 | 高速 |
| 可読性 | 高い | 低い(バイナリ) |
| スキーマ | 任意 | .proto で必須定義 |
| 後方互換性 | 設計次第 | タグ番号で保証しやすい |
.proto ファイルの基本構造
syntax = "proto3"; // バージョン指定(必須) package myapp; // パッケージ名(名前衝突を防ぐ) option go_package = "./pb"; // 言語固有のオプション message User { int32 id = 1; string name = 2; string email = 3; }
スカラー型
| proto型 | Go | Python | Java | 備考 |
|---|---|---|---|---|
double |
float64 |
float |
double |
64bit浮動小数点 |
float |
float32 |
float |
float |
32bit浮動小数点 |
int32 |
int32 |
int |
int |
負の数には非効率 |
int64 |
int64 |
int |
long |
負の数には非効率 |
uint32 |
uint32 |
int |
int |
符号なし32bit |
uint64 |
uint64 |
int |
long |
符号なし64bit |
sint32 |
int32 |
int |
int |
負の数に効率的 |
sint64 |
int64 |
int |
long |
負の数に効率的 |
bool |
bool |
bool |
boolean |
|
string |
string |
str |
String |
UTF-8エンコード |
bytes |
[]byte |
bytes |
ByteString |
任意バイト列 |
タグ番号のルール
フィールドに付く = 1・= 2 はタグ番号(フィールド番号)です。
message User { int32 id = 1; string name = 2; string email = 3; }
- バイナリエンコード時の識別子として使われる
- 一度リリースしたら変更・再利用してはいけない(後方互換性が壊れる)
- 1〜15は1バイト、16〜2047は2バイトで表現される(よく使うフィールドは小さい番号に)
- 19000〜19999はprotobufが予約しているため使用不可
repeated(配列)
message ListUsersResponse { repeated User users = 1; repeated string tags = 2; }
Go では []User、Python では list に対応します。
map(辞書型)
message Config { map<string, string> labels = 1; map<string, int32> scores = 2; }
- キーに使える型:整数型・
bool・string(浮動小数点・bytes・messageは不可) - 値にはほぼすべての型が使える
- 順序は保証されない
ネストしたメッセージ
message Order { int32 id = 1; User user = 2; repeated OrderItem items = 3; } message OrderItem { int32 product_id = 1; int32 quantity = 2; float price = 3; }
メッセージ内にメッセージを定義することもできます。
message Order { message Item { int32 product_id = 1; int32 quantity = 2; } int32 id = 1; repeated Item items = 2; }
enum(列挙型)
enum Status { STATUS_UNSPECIFIED = 0; // proto3では0始まりが必須 STATUS_ACTIVE = 1; STATUS_INACTIVE = 2; STATUS_DELETED = 3; } message User { int32 id = 1; string name = 2; Status status = 3; }
proto3では enum の最初の値は必ず 0 にする必要があります。慣習として XXX_UNSPECIFIED = 0 と書くことでデフォルト・未設定を明示します。
oneof(排他的フィールド)
複数フィールドのうち最大1つだけ値を持つ場合に使います。
message Notification { string title = 1; oneof content { string text_message = 2; string html_message = 3; bytes image_content = 4; } }
設定すると他のフィールドはクリアされます。
import(他のファイルを読み込む)
// user.proto syntax = "proto3"; package myapp; message User { int32 id = 1; string name = 2; }
// order.proto syntax = "proto3"; package myapp; import "user.proto"; message Order { int32 id = 1; User user = 2; }
well-known types(Googleが用意した標準型)
import "google/protobuf/timestamp.proto"; import "google/protobuf/empty.proto"; message Event { string name = 1; google.protobuf.Timestamp created_at = 2; } // 引数なし・戻り値なし rpc DeleteAll (google.protobuf.Empty) returns (google.protobuf.Empty);
主なwell-known types:
| 型 | 用途 |
|---|---|
Timestamp |
日時(Unix時刻) |
Duration |
期間 |
Empty |
引数・戻り値なし |
Any |
任意のメッセージ |
Int32Value / StringValue など |
null と未設定を区別するラッパー |
デフォルト値
proto3ではフィールドに値を設定しない場合、型ごとのデフォルト値になります。
| 型 | デフォルト値 |
|---|---|
| 数値型 | 0 |
bool |
false |
string |
"" |
bytes |
空バイト列 |
enum |
最初の値(0 の値) |
message |
nil / null |
repeated |
空のリスト |
null と未設定を区別したい場合は optional キーワードを使います。
message User { optional string nickname = 4; // null と "" を区別できる }
フィールドの削除と予約(reserved)
リリース済みのフィールドを削除するときは reserved でタグ番号と名前を予約します。これにより後から同じ番号・名前を誤って再利用することを防げます。
message User { reserved 4, 5; reserved "old_field"; int32 id = 1; string name = 2; string email = 3; // 4と5は削除済み }
JSON との相互変換
Go
import "google.golang.org/protobuf/encoding/protojson" // proto → JSON jsonBytes, _ := protojson.Marshal(user) fmt.Println(string(jsonBytes)) // {"id":1,"name":"田中","email":"tanaka@example.com"} // JSON → proto user := &pb.User{} protojson.Unmarshal(jsonBytes, user)
Python
from google.protobuf import json_format # proto → JSON json_str = json_format.MessageToJson(user) # JSON → proto user = json_format.Parse(json_str, pb.User())
まとめ
| 構文 | 用途 |
|---|---|
message |
データ型の定義 |
repeated |
配列フィールド |
map<K, V> |
辞書フィールド |
enum |
列挙型 |
oneof |
排他的フィールド |
import |
他の .proto を読み込む |
reserved |
削除済みフィールドを保護 |
optional |
null と未設定を区別する |
- タグ番号は変更・再利用しない(後方互換性の要)
- フィールドを削除するときは
reservedで保護する - 頻繁に使うフィールドはタグ番号1〜15に割り当てる(エンコードサイズ節約)
gRPCでのサービス定義と実装方法は「gRPC入門:Protocol Buffersの定義からサーバー・クライアント実装まで」を参照してください。