摘要:優(yōu)點(diǎn)在谷歌內(nèi)部長期使用產(chǎn)品成熟度高跨語言支持多種語言包括和編碼后的消息更小更加有利于存儲(chǔ)和傳輸編解碼的性能非常高支持不同協(xié)議版本的前向兼容支持定義可選和必選字段的入門是一個(gè)靈活高效結(jié)構(gòu)化的數(shù)據(jù)序列化框架相比與等傳統(tǒng)的序列化工具它更小更快更簡
Google Protobuf 優(yōu)點(diǎn):
在谷歌內(nèi)部長期使用, 產(chǎn)品成熟度高.
跨語言、支持多種語言, 包括 C++、Java 和 Python.
編碼后的消息更小, 更加有利于存儲(chǔ)和傳輸.
編解碼的性能非常高.
支持不同協(xié)議版本的前向兼容.
支持定義可選和必選字段.
Protobuf 的入門Protobuf 是一個(gè)靈活、高效、結(jié)構(gòu)化的數(shù)據(jù)序列化框架, 相比與 xml 等傳統(tǒng)的序列化工具, 它更小、更快、更簡單.
Protobuf 支持?jǐn)?shù)據(jù)結(jié)構(gòu)化一次可以到處使用, 甚至跨語言使用, 通過代碼生成工具可以自動(dòng)生成不同語言版本的源代碼, 甚至可以在使用不同版本的數(shù)據(jù)結(jié)構(gòu)進(jìn)程間進(jìn)行數(shù)據(jù)傳遞, 實(shí)現(xiàn)數(shù)據(jù)結(jié)構(gòu)前向兼容.
定義消息類型syntax = "proto3"; message SearchRequest { string query = 1; int32 page_number = 2; int32 result_per_page = 3; }
該文件的第一行指定使用 proto3 語法, 如果不寫的話表示 proto2.
分配字段編號(hào)string query = 1; 1 就是字段編號(hào), 字段號(hào)主要用來標(biāo)識(shí)二進(jìn)制格式字段的. 1 到 15 字段號(hào)占一個(gè)字節(jié). 16 到 2047 字段號(hào)需要兩個(gè)字節(jié).
我們將對(duì)象轉(zhuǎn)換為報(bào)文的時(shí)候, 是按照字段編號(hào)進(jìn)行報(bào)文封裝的; 我們接收到數(shù)據(jù)之后框架會(huì)幫我們按照字段號(hào)進(jìn)行賦值.
不能使用數(shù)字19000到19999, 因?yàn)樗鼈兪菫?Google Protobuf 保留的.
字段類型對(duì)應(yīng).proto Type | Notes | C++ Type | Java Type |
---|---|---|---|
double | double | double | |
float | float | float | |
int32 | 使用可變長度編碼, 對(duì)負(fù)數(shù)編碼效率低下 如果您的字段可能有負(fù)值, 則使用sint32代替. |
int32 | int |
int64 | 使用可變長度編碼, 對(duì)負(fù)數(shù)編碼效率低下 如果您的字段可能有負(fù)值, 則使用sint64代替. |
int64 | long |
uint32 | 使用可變長度編碼 | uint32 | int |
uint64 | 使用可變長度編碼 | uint64 long | |
sint32 | 使用可變長度編碼 有符號(hào)的int值這些編碼比常規(guī)int32更有效地編碼負(fù)數(shù) |
uint32 | int |
sint64 | 使用可變長度編碼 有符號(hào)的int值這些編碼比常規(guī)int64更有效地編碼負(fù)數(shù) |
int64 | long |
fixed32 | 四個(gè)字節(jié), 如果值通常大于2的28次方, 則比uint32更有效 | uint32 | int |
fixed64 | 四個(gè)字節(jié), 如果值通常大于2的56次方, 則比uint64更有效 | uint64 | long |
sfixed32 | 四個(gè)字節(jié) | int32 | int |
sfixed64 | 四個(gè)字節(jié) | int64 | long |
bool | bool | boolean | |
string | 字符串必須始終包含UTF-8編碼或7位ASCII文本 | string | String |
bytes | 字符串必須始終包含UTF-8編碼或7位ASCII文本 | string | ByteString |
對(duì)于字符串, 默認(rèn)值是空字符串.
對(duì)于字節(jié), 默認(rèn)值為空字節(jié).
對(duì)于bool, 默認(rèn)值為false.
對(duì)于數(shù)字類型, 默認(rèn)值為零.
對(duì)于枚舉, 默認(rèn)值是第一個(gè)定義的枚舉值, 必須為0.
還請(qǐng)注意, 如果消息字段設(shè)置為默認(rèn)值, 則該值將不會(huì)序列化.允許嵌套
Protocol Buffers 定義 message 允許嵌套組合成更加復(fù)雜的消息
message SearchResponse { repeated Result results = 1; } message Result { string url = 1; string title = 2; repeated string snippets = 3; }
更多的例子:
message SearchResponse { message Result { string url = 1; string title = 2; repeated string snippets = 3; } repeated Result results = 1; } message SomeOtherMessage { SearchResponse.Result result = 1; }
message Outer { // Level 0 message MiddleAA { // Level 1 message Inner { // Level 2 int64 ival = 1; bool booly = 2; } } message MiddleBB { // Level 1 message Inner { // Level 2 int32 ival = 1; bool booly = 2; } } }導(dǎo)入定義
可以在文件的頂部添加一個(gè)import語句:
import "myproject/other_protos.proto";未知字段
未知字段就是解析器無法識(shí)別的字段. 例如, 當(dāng)服務(wù)端使用新消息發(fā)送數(shù)據(jù), 客戶端使用舊消息解析數(shù)據(jù), 那么這些新字段將成為舊消息中的未知字段.
在3.5和更高版本中, 未知字段在解析過程中被保留, 并包含在序列化中輸出.
Map 類型repeated 類型可以用來表示數(shù)組, Map 類型則可以用來表示字典.
mapmap_field = N; map projects = 3;
key_type 可以是任何 int 或者 string 類型(任何的標(biāo)量類型, 具體可以見上面標(biāo)量類型對(duì)應(yīng)表格, 但是要除去 float、double 和 bytes)
枚舉值也不能作為 key.
key_type 可以是除去 map 以外的任何類型.
需要特別注意的是:
map 是不能用 repeated 修飾的.
map 迭代順序的是不確定的, 所以你不能確定 map 是一個(gè)有序的.
為 .proto 生成文本格式時(shí), map 按 key 排序. 數(shù)字的 key 按數(shù)字排序.
從數(shù)組中解析或合并時(shí), 如果有重復(fù)的 key, 則使用所看到的最后一個(gè) key(覆蓋原則).從文本格式解析映射時(shí), 如果有重復(fù)的 key, 解析可能會(huì)失敗.
Protocol Buffer 雖然不支持 map 類型的數(shù)組, 但是可以轉(zhuǎn)換一下, 用以下思路實(shí)現(xiàn) maps 數(shù)組:
message MapFieldEntry { key_type key = 1; value_type value = 2; } repeated MapFieldEntry map_field = N;
上述寫法和 map 數(shù)組是完全等價(jià)的,所以用 repeated 巧妙的實(shí)現(xiàn)了 maps 數(shù)組的需求.
Protocol Buffer 命名規(guī)范message 采用駝峰命名法. message 首字母大寫開頭. 字段名采用下劃線分隔法命名.
message SongServerRequest { required string song_name = 1; }
枚舉類型采用駝峰命名法. 枚舉類型首字母大寫開頭. 每個(gè)枚舉值全部大寫, 并且采用下劃線分隔法命名.
enum Foo { FIRST_VALUE = 0; SECOND_VALUE = 1; }
每個(gè)枚舉值用分號(hào)結(jié)束, 不是逗號(hào).
服務(wù)名和方法名都采用駝峰命名法. 并且首字母都大寫開頭.
service FooService { rpc GetSomething(FooRequest) returns (FooResponse); }常用方法
getDefaultInstance(): 返回單例實(shí)例, 它與 newBuilder().build() 實(shí)例相同
getDescriptor(): 返回類型的描述符. 包括具有哪些字段以及類型. 這可以與 Message 的反射方法一起使用, 例如getField().
parseFrom(...): 返回反序列化后的 Message. 注意不會(huì)拋出 UninitializedMessageException 和 InvalidProtocolBufferException 異常.
Message.Builder: 中的 mergeFrom() 放會(huì)將數(shù)據(jù)解析為此類型的消息, 并進(jìn)行消息合并.
newBuilder(): 創(chuàng)建一個(gè)新的構(gòu)建器.
Any類型允許包裝任意的message類型:
import "google/protobuf/any.proto"; message Response { google.protobuf.Any data = 1; }總結(jié)
message SubscribeReq { int32 subReqID = 1; string userName = 2; string productName = 3; string address = 4; }
可以通過 pack() 和 unpack()(方法名在不同的語言中可能不同)方法裝箱/拆箱,以下是Java的例子:
People people = People.newBuilder().setName("proto").setAge(1).build(); // protoc編譯后生成的message類 Response r = Response.newBuilder().setData(Any.pack(people)).build(); // 使用Response包裝people System.out.println(r.getData().getTypeUrl()); // type.googleapis.com/example.protobuf.people.People System.out.println(r.getData().unpack(People.class).getName()); // protoOneof
如果你有一些字段同時(shí)最多只有一個(gè)能被設(shè)置, 可以使用 oneof 關(guān)鍵字來實(shí)現(xiàn), 任何一個(gè)字段被設(shè)置, 其它字段會(huì)自動(dòng)被清空(被設(shè)為默認(rèn)值):
message SampleMessage { oneof test_oneof { string name = 4; SubMessage sub_message = 9; } }默認(rèn)值
比如我們創(chuàng)建了上面的消息類型, 我們?cè)诖a中設(shè)置 builder.setSubReqID(0); 為 0, 零是數(shù)值類型的默認(rèn)值; 所以我們會(huì)看到序列化后的數(shù)據(jù)中, 沒有對(duì)此字段進(jìn)行序列化.
byte[] arry = builder.build().toByteArray();
arry 長度為 0. 對(duì)于字段類型是 string 類型的也是一樣的; 也就是說顯示賦值默認(rèn)值也不會(huì)對(duì)其進(jìn)行序列化.
保留字段message SubscribeReq { reserved 2; int32 subReqID = 1; string userName = 2; string productName = 3; string address = 4; }
顧名思義, 就是此字段會(huì)被保留可能在以后會(huì)使用此字段. 使用關(guān)鍵字 reserved 表示我要保留字段數(shù) 2.
上面代碼我們?cè)谏?Java 文件的時(shí)候會(huì)出現(xiàn) ubscribeReqPeoro.proto: Field "userName" uses reserved number 2 錯(cuò)誤信息, 所以我們需要將 string userName = 2; 注釋, 或者刪除.
保留后我們無法對(duì)其設(shè)置或序列化和反序列化.
文章版權(quán)歸作者所有,未經(jīng)允許請(qǐng)勿轉(zhuǎn)載,若此文章存在違規(guī)行為,您可以聯(lián)系管理員刪除。
轉(zhuǎn)載請(qǐng)注明本文地址:http://m.specialneedsforspecialkids.com/yun/72796.html
摘要:基于提供的對(duì)象輸入輸出流和可以直接把對(duì)象作為可存儲(chǔ)的字節(jié)數(shù)組寫入文件也可以傳輸?shù)骄W(wǎng)絡(luò)上序列化的目的主要有兩個(gè)網(wǎng)絡(luò)傳輸對(duì)象持久化當(dāng)進(jìn)行遠(yuǎn)程跨進(jìn)程服務(wù)調(diào)用時(shí)需要把被傳輸?shù)膶?duì)象編碼為字節(jié)數(shù)組或者對(duì)象而當(dāng)遠(yuǎn)程服務(wù)讀取到對(duì)象或字節(jié)數(shù)組時(shí)需要將其解碼為 基于 Java 提供的對(duì)象輸入/輸出流 ObjectInputStream 和 ObjectOutputStream, 可以直接把 Java 對(duì)象...
摘要:是一個(gè)面向字節(jié)流的協(xié)議,它是性質(zhì)是流式的,所以它并沒有分段。可基于分隔符解決。編解碼的主要目的就是為了可以編碼成字節(jié)流用于在網(wǎng)絡(luò)中傳輸持久化存儲(chǔ)。 showImg(https://segmentfault.com/img/remote/1460000015895049); 前言 記得前段時(shí)間我們生產(chǎn)上的一個(gè)網(wǎng)關(guān)出現(xiàn)了故障。 這個(gè)網(wǎng)關(guān)邏輯非常簡單,就是接收客戶端的請(qǐng)求然后解析報(bào)文最后發(fā)送...
摘要:等之所以支持跨語言,是因?yàn)樗麄冏约憾x了一套結(jié)構(gòu)化數(shù)據(jù)存儲(chǔ)格式,如的,用于編解碼對(duì)象,作為各個(gè)語言通信的中間協(xié)議。 前段時(shí)間覺得自己一直用別人的框架,站在巨人的肩膀上,也該自己造造輪子了 一時(shí)興起 就著手寫起了RPC框架 這里寫了系列博客拿給大家分享下 這篇是開篇的思路篇 項(xiàng)目最終的代碼放在了我的github上https://github.com/wephone/Me... 歡迎sta...
摘要:結(jié)構(gòu)作為服務(wù)端作為序列化數(shù)據(jù)的協(xié)議前端通訊演示地址服務(wù)端實(shí)現(xiàn)啟動(dòng)類長連接示例主線程組從線程組請(qǐng)求的解碼和編碼把多個(gè)消息轉(zhuǎn)換為一個(gè)單一的或是,原因是解碼器會(huì)在每個(gè)消息中生成多個(gè)消息對(duì)象主要用于處理大數(shù)據(jù)流,比如一個(gè)大小的文件如果你直接傳輸肯定 結(jié)構(gòu) netty 作為服務(wù)端 protobuf 作為序列化數(shù)據(jù)的協(xié)議 websocket 前端通訊 演示 GitHub 地址 showImg(...
摘要:結(jié)構(gòu)作為服務(wù)端作為序列化數(shù)據(jù)的協(xié)議前端通訊演示地址服務(wù)端實(shí)現(xiàn)啟動(dòng)類長連接示例主線程組從線程組請(qǐng)求的解碼和編碼把多個(gè)消息轉(zhuǎn)換為一個(gè)單一的或是,原因是解碼器會(huì)在每個(gè)消息中生成多個(gè)消息對(duì)象主要用于處理大數(shù)據(jù)流,比如一個(gè)大小的文件如果你直接傳輸肯定 結(jié)構(gòu) netty 作為服務(wù)端 protobuf 作為序列化數(shù)據(jù)的協(xié)議 websocket 前端通訊 演示 GitHub 地址 showImg(...
閱讀 1783·2023-04-25 22:42
閱讀 2214·2021-09-22 15:16
閱讀 3493·2021-08-30 09:44
閱讀 490·2019-08-29 16:44
閱讀 3310·2019-08-29 16:20
閱讀 2517·2019-08-29 16:12
閱讀 3390·2019-08-29 16:07
閱讀 670·2019-08-29 15:08