protobuf的誕生
以前在不同系統(tǒng)或者不同項(xiàng)目之間傳輸數(shù)據(jù)主要使用XML格式。下面是xml格式的一些優(yōu)缺點(diǎn)。
時(shí)間開銷:XML格式化(序列化)的開銷倒還好;但是XML解析(反序列化)的開銷就不敢恭維啦。俺之前經(jīng)常碰到一些時(shí)間性能很敏感的場(chǎng)合,由于不堪忍受XML解析的速度,棄之如敝履。
空間開銷:雖然XML格式為了有較好的可讀性,但是引入了一些冗余的文本信息。所以空間開銷也不是太好。
由于Google公司賴以吹噓的就是它的海量數(shù)據(jù)和海量處理能力。對(duì)于幾十萬、上百萬機(jī)器的集群,動(dòng)不動(dòng)就是PB級(jí)的數(shù)據(jù)量,哪怕性能稍微提高0.1%也是相當(dāng)可觀滴。所以Google自然無法容忍XML在性能上的明顯缺點(diǎn)。再加上Google從來就不缺造輪子的牛人,所以protobuf也就應(yīng)運(yùn)而生了。
什么是protobuf?
protobuf的開源項(xiàng)目地址
protobuf是一種輕便高效的結(jié)構(gòu)化數(shù)據(jù)存儲(chǔ)格式,全稱是protocol buffers;可以用于結(jié)構(gòu)化數(shù)據(jù)串行化,或者說序列化。它很適合做數(shù)據(jù)存儲(chǔ)或 RPC 數(shù)據(jù)交換格式??捎糜谕ㄓ崊f(xié)議、數(shù)據(jù)存儲(chǔ)等領(lǐng)域的語言無關(guān)、平臺(tái)無關(guān)、可擴(kuò)展的序列化結(jié)構(gòu)數(shù)據(jù)格式。
protobuf數(shù)據(jù)格式的定義
在data.proto文件中用proto3的語法封裝自己的數(shù)據(jù),如下:

代碼格式和c語言風(fēng)格差不多,syntax表示你要用的proto語法版本,默認(rèn)是proto2,message是關(guān)鍵詞,Person則是自定義的數(shù)據(jù)結(jié)構(gòu),里面是定義的一些屬性。
關(guān)于proto2的語法,可參考:Protobuf 語法指南
proto3的語法格式,可參考:protobuf3語法
這樣,我們就定義好了一個(gè).proto文件。接下來運(yùn)行g(shù)o get -u github.com/golang/protobuf/protoc-gen-go命令將protoc工具安裝在$PATH/bin中,安裝好之后,在data.proto目錄下打開終端,輸入protoc --go_out=. *.proto ?會(huì)在同目錄下生成data.pb.go文件,這個(gè)文件中包含了如下的函數(shù):
(1) func(m*Person)Reset()? //重置
(2)func(m*Person) String() string ? ?//將Person轉(zhuǎn)化為字符串
(3)func(*Person)ProtoMessage() ? ?//空函數(shù)
(4)func(*Person)Descriptor()([]byte,[]int) ? ?//返回該文件的描述符和一個(gè)[]int{0}的切片
其中(1)(2)(3)函數(shù)實(shí)現(xiàn)了Message接口,見lib.go

現(xiàn)在在main函數(shù)來將Person對(duì)象進(jìn)行編碼和解碼,見代碼
packagemain
import(
? ? "fmt"
? ? "log"
? ? data ?"protobuf3_test/data" ?//導(dǎo)入data.pb.go所在的文件目錄
? ? "github.com/golang/protobuf/proto"
)
func ?main(){
? ? person := &data.Person{
? ? ? ? ?"初級(jí)賽亞人",
? ? ? ? ?"男",
? ? ? ? ?117,
? ? }
? ? protoData,err := proto.Marshal(person) ? ?//將person編碼成protobuf格式的數(shù)據(jù)
? ? if err != nil {
? ? ? ? log.Fatal("marshalingerror:",err)
? ? }
? ? fmt.Println("protobuf編碼之后的數(shù)據(jù)=",protoData)
? ? newPerson := &data.Person{}
? ? err = proto.Unmarshal(protoData,newPerson) ? ?//將protobuf的解碼存入newPerson中
? ? if err != nil {
? ? ? ? ? ?log.Fatal("unmarshalingerror:",err)
? ? }
? ? fmt.Println("protobuf解碼之后的數(shù)據(jù)=",newPerson)
? ? fmt.Println("newPerson.Name=",newPerson.Name)
? ? fmt.Println("newPerson.Sex=",newPerson.Sex)
? ? fmt.Println("newPerson.Age=",newPerson.Age)
? ? if person.String() != newPerson.String() {
? ? ? ? log.Fatalf("datamismatch%q!=%q",person.String(),newPerson.String())
? ? }
}
控制臺(tái)打印的結(jié)果是:

至此完成了protobuf的編碼與解碼