Protobuf入門
在Kafka中,發送的消息是位元組數組,因此就需要一個方法來將消息對象序列化為位元組數組,在消費者端再反序列化為對象。最常用的序列化格式就是JSON了。雖然JSON對人類非常友好,但是對於機器來說,更容易進行序列化和反序列化的格式還是二進位的格式。
Protobuf(Protocol buffers)是由Google開發的一種二進位協議,用於對結構化數據進行序列化和反序列化。這種格式佔用空間更少,更加簡單易於維護,同時擁有更好的性能。對於機器之間的通訊,Protobuf是比XML和JSON等格式更好的一種選擇。
Protobuf的使用相比之下更加複雜,需要編寫.proto
格式的文件來定義數據格式,之後通過protoc
編譯器將其編譯到對應的語言,之後再在程式中引用。
使用
安裝
首先就是安裝protoc
編譯器,這個編譯器可以直接到github上下載二進位包,解壓到對應位置並設置PATH
即可。
之後就是安裝對應語言的客戶端,對於golang,執行下面兩條語句安裝就可以了:
go get github.com/golang/protobuf/proto
go get github.com/golang/protobuf/protoc-gen-go
編譯
之後新建一個.proto
文件,在其中定義消息的格式:
syntax = "proto3";
package test;
option go_package = "proto/test";
message Award {
int64 uid = 1;
int64 awardId = 2;
string userName = 3;
}
然後使用protoc
編譯器將其編譯到go文件就行了:
protoc --go_out=. proto/*.proto
在go程式中引入生成的包就可以進行序列化和反序列化了:
import pb "proto/test"
func marshal() {
award := &pb.Award{
Uid: 628,
AwardId: 1,
UserName: "Haruka",
}
msg, err := proto.Marshal(award)
}
func unmarshal() {
award := &pb.Award{}
if err := proto.Unmarshal(msg.Value, award); err != nil {
panic(err)
}
}
proto3
語言
在.proto
文件的第一行就是syntax = "proto3";
,用於聲明該文件是proto3
版本的。之後可以聲明package
用於避免命名衝突,最後就可以定義message
了。
欄位ID
message
的每個欄位都要分配一個唯一的ID,最小是1
最大是2^29 - 1
,同時不能使用 1900019999的ID。ID可以任意分配,但是115隻會佔用1個位元組,而16~2047會佔用2個位元組,因此應盡量從小開始分配,並將小的分配給最經常出現的欄位。
可以使用reserved
來對欄位名和ID進行保留,一般用於為未來新增欄位保留,或者保留刪除欄位來避免之後的欄位使用,防止發生衝突:
message Foo {
reserved 2, 15, 9 to 11;
reserved "foo", "bar";
}
欄位類型
欄位的類型默認是singular,也就是只能出現一次或0次,如果在欄位聲明前加上repeated
的話就可以出現多次。
protobuf的標量類型有以下幾種:double
float
int32
int64
uint32
uint64
sint32
sint64
fixed32
fixed64
sfixed32
sfixed64
bool
string
bytes
。
protobuf中還可以使用枚舉和複合類型。例如可以通過import "google/protobuf/timestamp.proto"
來使用timestamp類型。
protobuf數據類型的默認值規則如下,如果一個欄位被設置為默認值,則其不會被序列化:
string
bytes
類型默認為空bool
類型默認為false
- 數值類型默認為
0
- 枚舉類型默認為第一個,即
0
- 複合類型取決於語言
枚舉類型
message SearchRequest {
enum Corpus {
option allow_alias = true;
UNIVERSAL = 0;
WEB = 1;
TEST = 1;
IMAGES = 2;
}
Corpus corpus = 4;
}
通過enum
來聲明一個枚舉,注意枚舉中必須要有0
值用來作為默認值,同時0
值應該是第一個元素,以與proto2
兼容。通過option allow_alias = true;
來允許兩個枚舉元素有相同的值,即這兩個元素可以相互替代。
複合類型
在一個message
中,可以嵌入其他的message
作為複合類型:
message SearchResponse {
repeated Result results = 1;
}
message Result {
string url = 1;
string title = 2;
repeated string snippets = 3;
}
在protobuf
中還有其他高級類型,如Any
oneof
map
等,就不詳細介紹了。