Google Protocol Buffer( 簡稱 Protobuf)是Google公司內部的混合語言數據標準,他們主要用於RPC系統和持續數據存儲系統。java
Protocol Buffers 是一種輕便高效的結構化數據存儲格式,能夠用於結構化數據串行化,或者說序列化。它很適合作數據存儲或RPC數據交換格式。可用於通信協議、數據存儲等領域的語言無關、平臺無關、可擴展的序列化結構數據格式。編程
簡單來講,Protobuf的功能相似於XML,即負責把某種數據結構的信息,以某種格式保存起來。主要用於數據存儲、傳輸協議等使用場景。爲何已經有了XLM,JSON等已經很廣泛的數據傳輸方式,還要設計出Protobuf這樣一種新的數據協議呢?數組
Protobuf優勢:
性能好/效率高數據結構
時間維度:採用XML格式對數據進行序列化時,時間消耗上性能尚可;對於使用XML格式對數據進行反序列化時的時間花費上,耗時長,性能差。編程語言
空間維度:XML格式爲了保持較好的可讀性,引入了一些冗餘的文本信息。因此在使用XML格式進行存儲數據時,也會消耗空間。分佈式
總體而言,Protobuf以高效的二進制方式存儲,比XML小3到10倍,快20到100倍。 代碼生成機制ide
代碼生成機制的含義在Go語言中,能夠經過定義結構體封裝描述一個對象,並構造一個新的結構體對象。好比定義Person結構體,並存放於Person.go文件:性能
type Person struct{ Name stringAge intSex int}
在分佈式系統中,由於程序代碼時分開部署,好比分別爲A、B。A系統在調用B系統時,沒法直接採用代碼的方式進行調用,由於A系統中不存在B系統中的代碼。所以,A系統只負責將調用和通訊的數據以二進制數據包的形式傳遞給B系統,由B系統根據獲取到的數據包,本身構建出對應的數據對象,生成數據對象定義代碼文件。這種利用編譯器,根據數據文件自動生成結構體定義和相關方法的文件的機制被稱做代碼生成機制。學習
代碼生成機制的優勢ui
首先,代碼生成機制可以極大解放開發者編寫數據協議解析過程的時間,提升工做效率;其次,易於開發者維護和迭代,當需求發生變動時,開發者只須要修改對應的數據傳輸文件內容便可完成全部的修改。
支持「向後兼容」和「向前兼容」
向後兼容:在軟件開發迭代和升級過程當中,"後"能夠理解爲新版本,越新的版本越靠後;而「前」意味着早起的版本或者先前的版本。向「後」兼容便是說當系統升級迭代之後,仍然能夠處理老版本的數據業務邏輯。
向前兼容:向前兼容便是系統代碼未升級,可是接受到了新的數據,此時老版本生成的系統代碼能夠處理接收到的新類型的數據。
支持先後兼容是很是重要的一個特色,在龐大的系統開發中,每每不可能統一完成全部模塊的升級,爲了保證系統功能正常不受影響,應最大限度保證通信協議的向前兼容和向後兼容。
支持多種編程語言 Protobuf不只僅Google開源的一個數據協議,還有不少種語言的開源項目實現。在Google官方發佈的Protobuf的源代碼中包含了C++、Java、Python三種語言。本系列課程中,咱們學習如何實現Golang語言中的功能實現。
Protobuf 缺點 可讀性較差爲了提升性能,Protobuf採用了二進制格式進行編碼。二進制格式編碼對於開發者來講,是沒辦法閱讀的。在進行程序調試時,比較困難。
缺少自描述 諸如XML語言是一種自描述的標記語言,即字段標記的同時就表達了內容對應的含義。而Protobuf協議不是自描述的,Protobuf是經過二進制格式進行數據傳輸,開發者面對二進制格式的Protobuf,沒有辦法知道所對應的真實的數據結構,所以在使用Protobuf協議傳輸時,必須配備對應的proto配置文件。
協議語法:
message:Protobuf中定義一個數據結構須要用到關鍵字message,這一點和Java的class,Go語言中的struct相似。標識號:在消息的定義中,每一個字段等號後面都有惟一的標識號,用於在反序列化過程當中識別各個字段的,一旦開始使用就不能改變。標識號從整數1開始,依次遞增,每次增長1,標識號的範圍爲1~(2^99)-1,其中[19000-19999]爲Protobuf協議預留字段,開發者不建議使用該範圍的標識號;一旦使用,在編譯時Protoc編譯器會報出警告。
字段規則:字段規則有三種:
一、required:該規則規定,消息體中該字段的值是必需要設置的。
二、optional:消息體中該規則的字段的值能夠存在,也能夠爲空,optional的字段能夠根據defalut設置默認值。repeated:消息體中該規則字段能夠存在多個(包括0個),該規則對應java的數組或者go語言的slice。
枚舉類型:proto協議支持使用枚舉類型,和正常的編程語言同樣,枚舉類型可使用enum關鍵字定義在.proto文件中:
enum Age{ //1,2表明字段順序male=1; female=2; }
字段默認值:.proto文件支持在進行message定義時設置字段的默認值,能夠經過default進行設置,以下所示:
message Address { required sint32 id = 1 [default = 1]; required string name = 2 [default = '北京']; optional string pinyin = 3 [default = 'beijing']; required string address = 4; required bool flag = 5 [default = true]; }
導入:若是須要引用的message是寫在別的.proto文件中,能夠經過import "xxx.proto"來進行引入:
嵌套:message與message之間能夠嵌套定義,好比以下形式:
syntax = "proto2"; package example; message Person { required string Name = 1; required int32 Age = 2; required string From = 3; optional Address Addr = 4; message Address {required sint32 id = 1; required string name = 2;optional string pinyin = 3; required string address = 4;}}
message更新規則
message定義之後若是須要進行修改,爲了保證以前的序列化和反序列化可以兼容新的message,message的修改須要知足如下規則:
不能夠修改已存在域中的標識號。
全部新增添的域必須是 optional 或者 repeated。
非required域能夠被刪除。可是這些被刪除域的標識號不能夠再次被使用。
非required域能夠被轉化,轉化時可能發生擴展或者截斷,此時標識號和名稱都是不變的。
sint32和sint64是相互兼容的。
fixed32兼容sfixed32。 fixed64兼容sfixed64。
optional兼容repeated。發送端發送repeated域,用戶使用optional域讀取,將會讀取repeated域的最後一個元素。
Protobuf 序列化後所生成的二進制消息很是緊湊,這得益於 Protobuf 採用的很是巧妙的 Encoding 方法。接下來看一看Protobuf協議是如何實現高效編碼的。
編輯好proto文件後,生成命令:
route_guide.proto
將結構體賦值,並序列化:
msg_test:=&service.Order{ OrderId: proto.Int32(1), Num: proto.Int64(1), Timestamp: proto.String("20191030"), } //序列化代碼 msg,err:=proto.Marshal(msg_test)//二進制 if err!=nil{ panic(err) } fmt.Println(msg)
反序列化操做:
//反序列化 var order service.Order err=proto.Unmarshal(msg,&order) //反序列化 if err!=nil{ panic(err) return } fmt.Println(order.GetOrderId())