Protocol Buffer: Basic Go
このチュートリアルはGoプログラマにprotocol buffersの使い方を紹介します。
使われるprotocol buffersのバージョンはproto3です。
簡単なサンプルアプリケーションの作成によって
- .protoファイル内でのmessageの定義の仕方
- protocol buffer compilerの使い方
- Go protocol buffer APIを用いたmessageの読み書きの仕方
を紹介します。
protocol bufferの詳細な情報を知りたい場合は以下へどうぞ
Protocol Buffer Language Guide
Go API Reference
Go Generated Code Guide
Encoding Reference
Why use protocol buffers?
ここで作るサンプルアプリケーションは、連絡先情報の書き込み、読み込みをファイル?との間でできるような簡単な連絡帳アプリケーションです。
それぞれの連絡先情報は
- 名前
- ID
- 電話番号
の情報を持っています。
これらのデータをシリアライズするには様々な方法があり、例えば
- goのパッケージgobを使う
- XMLを使う
などがあります。
Protocol buffersはこのシリアライズの問題に対して柔軟で, 効率的, 自動的 な解決策を示すことができます。
.proto
ファイルに保存したいデータを記述するとProtocol bufferのコンパイラは効率的なバイナリ形式でProtocol Bufferデータの自動エンコーディングと解析を実装します。
さらに、Protocol Bufferは以前のフォーマットで符号化されたデータを読み取れるようにフォーマットを拡張していく考え方に元付いて作られています。
Defining your protocol format
サンプルアプリケーションを作っていきましょう。
まず、.proto
ファイルを作る必要があります。
.protoファイルにはシリアライズしたいデータを追加していきます。
このサンプルではaddressbook.proto
を作りましょう
.proto
ファイルはパッケージの宣言から始まります、これは異なるプロジェクト間のネームスペースの競合を防ぐのに役立ちます。
syntax = "proto3"; // 使うバージョンの指定
package tutorial // パッケージの宣言
パッケージ名はgo_package
を指定しない限り、goパッケージとして解釈されます。
go_packageを使用する際もネームスペースの競合が起きないようにpackageを宣言する必要があります。
次にメッセージを定義します。
メッセージは型付きのフィールドを含む集合体です。
bool
,int32
,float
,double
,string
など多くの標準的なデータ型をフィールドの型として指定することができます。
他のメッセージタイプをフィールドタイプとして宣言し、メッセージに構造を持たせることも可能です
syntax = "proto3";
package tutorial;
message Person {
string name = 1;
int32 id = 2;
string email = 3;
enum PhoneType {
MOBILE = 0;
HOME = 1;
WORK = 2;
}
message PhoneNumber {
string number = 1;
PhoneType type = 2;
}
repeated PhoneNumber phones = 4;
}
message AddressBook {
repeated Person people = 1;
}
上記のサンプルではPerson message
にはPhoneNumber message
が含まれ、AddressBook message
にはPerson message
が含まれています。
上記のようにネストされた他のメッセージもフィールドタイプとして定義することができます。
さらに、列挙型を定義する事も可能です。
各要素に書かれている『= 1』『= 2』などはフィールドがバイナリで使用する際に一意のタグを識別する為に必要です
もしフィールドの値がセットされていなかった時にはデフォルト(数値型: 0, String: "", bool: false, )が使用されます
フィールドがrepeated
の場合、フィールドを任意の回数繰り返す事ができます。
繰り返される値の順序はProtocol bufferに保存されます。
repeated
フィールドは動的サイズの配列と考えることができます。
.proto
ファイルの完璧なガイドを見たい場合は次のサイトを参照して見てください。
Protocol Buffer Language Guide
Compiling your protocol buffers
次は先ほど定義した.proto
ファイルをコンパイルさせ定義したクラスなどを生成させていきます。
これにはprotoc
コマンドを使います。
-
protocコマンドがインストールされていない場合はこちらよりダウンロードしてください。
Download the package -
次のコマンドを入力してGoのprotocol buffersプラグインをインストールしてください。
go get -u github.com/golang/protobuf/protoc-gen-go
- コンパイラを実行します。 ソースコードディレクトリ(指定しなかった場合はカレントディレクトリ)、宛先ディレクトリ(生成されたコードをどこに置くかを指定します。 多くの場合$SRC_DIRと同じです)の指定と
.proto
ファイルのパスの指定を行います。
protoc -I=$SRC_DIR --go_out=$DST_DIR $SRC_DIR/addressbook.proto
ex
|
-addressbook.proto
の場合は
protoc -I=./ --go_out=./ addressbook.proto
のようになります。
The Protocol Buffer API
生成されたaddressbook.pb.go
によって
- Peopleフィールドを含んだAddressBook
- Name, Id, Email, Phonesを含んだPerson
- Number, Typeを含んだPerson_PhoneNumber
- enum型のPerson.PhoneTypeを含んだ
Person_PhoneType
のような便利な型を使えるようになります。
ここで何を作られているのか詳しく知りたい場合はGo Generated Code guideを呼んでください。
Personインスタンス作成の例は次のような感じです。(Go)
p := pb.Person{
Id: 1234,
Name: "John Doe",
Email: "jdoe@example.com",
Phones: []*pb.Person_PhoneNumber{
{Number: "555-4321", Type: pb.Person_HOME},
},
}
Writing a Message
protocol buffersを使う目的はデータのシリアライズでした。
GoではprotoライブラリのMarshal関数を使うことによりデータをシリアライズすることができます。
proto.Marshal
を呼ぶとシリアライズされたprotocol bufferが帰ってきます。
例
book := &pb.AddressBook{}
// ...
// Write the new address book back to disk.
out, err := proto.Marshal(book)
if err != nil {
log.Fatalln("Failed to encode address book:", err)
}
if err := ioutil.WriteFile(fname, out, 0644); err != nil {
log.Fatalln("Failed to write address book:", err)
}
Reading a Message
エンコードされたメッセージをパースするにはprotoライブラリのUnmarshal関数を使います。
これを呼び出すとbufのデータをprotocol bufferとして解析し、結果をpbに格納することができます。
例
// Read the existing address book.
in, err := ioutil.ReadFile(fname)
if err != nil {
log.Fatalln("Error reading file:", err)
}
book := &pb.AddressBook{}
if err := proto.Unmarshal(in, book); err != nil {
log.Fatalln("Failed to parse address book:", err)
}
これでチュートリアルは完了です
お疲れ様でした。
さらに詳しい情報を知りたい方は次のリンクを見てみてください。
Protocol Buffer Language Guide