快来看看Google出品的Protocol Buffer,别仅仅会用Json和XML了
前言
- 習慣用 Json、XML 數(shù)據(jù)存儲格式的你們,相信大多都沒聽過Protocol Buffer
- Protocol Buffer 事實上 是 Google出品的一種輕量 & 高效的結(jié)構化數(shù)據(jù)存儲格式,性能比 Json、XML 真的強!太!
多!
由于 Google出品,我相信Protocol Buffer已經(jīng)具備足夠的吸引力 - 今天,我將獻上一份 Protocol Buffer的介紹 & 使用攻略,希望你們會喜歡。
文件夾
1. 定義
一種 結(jié)構化數(shù)據(jù) 的數(shù)據(jù)存儲格式(相似于 `XML、Json` )2. 作用
通過將 結(jié)構化的數(shù)據(jù) 進行 串行化(**序列化**),從而實現(xiàn) **數(shù)據(jù)存儲 / RPC 數(shù)據(jù)交換**的功能3. 特點
- 對照于 常見的 XML、Json 數(shù)據(jù)存儲格式。Protocol Buffer有例如以下特點:
4. 應用場景
數(shù)據(jù)傳輸量大 & 網(wǎng)絡環(huán)境不穩(wěn)定 的數(shù)據(jù)存儲、RPC 數(shù)據(jù)交換 的需求場景
如 即時IM (QQ、微信)的需求場景
總結(jié)
在 數(shù)據(jù)傳輸量較大的需求場景下,Protocol Buffer比XML、Json 更小、更快、使用 & 維護更簡單!
5. 使用流程
使用 Protocol Buffer 的流程例如以下:
5.1 環(huán)境配置
要使用Protocol Buffer 。須要先在電腦上安裝Protocol Buffer
整個 安裝過程 僅僅須要依照以下步驟進行就可以:
整個安裝過程請 自備梯子 以保證 網(wǎng)絡暢通
步驟1:下載 Protocol Buffer 安裝包
- 下載方式1:官網(wǎng)下載(須要FQ)
- 下載方式2:貼心的我 已經(jīng)給你們準備好了,請移步百度網(wǎng)盤。password:paju
此處選擇 較穩(wěn)定的版本號 protobuf-2.6.1.tar.gz 進行演示
下載成功后,對文件進行解壓,例如以下圖:
步驟2:安裝 HOMEBREW(已安裝的能夠跳過)
// 打開 終端 輸入以下指令 /usr/bin/ruby -e "$(curl -fsSL https://raw.githubusercontent.com/Homebrew/install/master/install)"步驟3:安裝 Protocol Buffer
打開 您的終端 依次輸入 下列指令 就可以:
brew install autoconf automake libtool curl // Step1:安裝 Protocol Buffer 依賴 // 注:Protocol Buffer 依賴于 autoconf、automake、libtool、curlcd Desktop/protobuf-2.6.1 // Step2:進入 Protocol Buffer安裝包 解壓后的文件夾(我的解壓文件放在桌面)./autogen.sh // Step3:執(zhí)行 autogen.sh 腳本./configure // Step4:執(zhí)行 configure.sh 腳本make // Step5:編譯未編譯的依賴包make check // Step6:檢查依賴包是否完整make install // Step7:開始安裝Protocol Buffer步驟4:檢查 Protocol Buffer 是否成功安裝
// 在 終端 下輸入 protoc - - version出現(xiàn) libprotoc 2.6.1 提示即表示 成功安裝。例如以下圖
特別注意:
- protoc = Protocol Buffer的編譯器
- 作用:將 .proto文件 編譯成相應平臺的 頭文件和源碼文件
- 在以下會詳細介紹
至此, Protocol Buffer已經(jīng)安裝完畢。以下將解說怎樣詳細使用Protocol Buffer
5.2 構建 Protocol Buffer 消息對象模型
5.2.1 構建步驟
以下將通過一個實例(Android(Java) 平臺為例)詳細介紹每一個步驟。
5.2.2 詳細介紹
- 實例說明:構建一個Person類的數(shù)據(jù)結(jié)構。包括成員變量name、id、email等等
- 平臺使用:以 Android(Java) 平臺為例來進行演示
步驟1:通過 Protocol Buffer 語法 描寫敘述 須要存儲的數(shù)據(jù)結(jié)構
- 新建一個文件,命名規(guī)則為:文件名稱 = 類名,后綴為 .proto
此處叫Demo.proto
- 依據(jù)上述數(shù)據(jù)結(jié)構的需求。在Demo.proto里 通過 Protocol Buffer 語法寫入相應 .proto對象模型的代碼,例如以下:
- 以下將結(jié)合 上述樣例 對 Protocol Buffer 語法 進行詳細介紹
關注1:包名
package protocobuff_Demo; // 關注1:包名- 作用:防止不同 .proto 項目間命名 發(fā)生沖突
- Protocol buffer包的解析過程例如以下:
- Protocol buffer 的類型名稱解析與 C++ 一致:從 最內(nèi)部 開始查找,依次 向外 進行
每一個包會被看作是其父類包的內(nèi)部類 - Protocol buffer 編譯器會解析 .proto文件里定義的全部類型名
- 生成器會依據(jù) 不同語言 生成 相應語言 的代碼文件
a. 即對 不同語言 使用了 不同的規(guī)則 進行處理
b. Protoco Buffer提供 C++、Java、Python 三種語言的 API
關注2:Option選項
option java_package = "com.carson.proto"; option java_outer_classname = "Demo"; // 關注2:option選項作用:影響 特定環(huán)境下 的處理方式
但不改變整個文件聲明的含義
經(jīng)常使用Option選項例如以下:
- 在 ProtocolBuffers 中同意 自己定義選項 并 使用
- 該功能屬于高級特性。使用頻率非常低,此處只是多描寫敘述。有興趣可查看官方文檔
關注3:消息模型
- 作用:真正用于描寫敘述 數(shù)據(jù)結(jié)構
- 組成:在 ProtocolBuffers 中:
- 一個 .proto 消息模型 = 一個 .proto文件 = 消息對象 + 字段
- 一個消息對象(Message) = 一個 結(jié)構化數(shù)據(jù)
- 消息對象(Message)里的 字段 = 結(jié)構化數(shù)據(jù) 里的成員變量
以下會詳細介紹 .proto 消息模型里的 消息對象 & 字段
1. 消息對象
在 ProtocolBuffers 中:
- 一個消息對象(Message) = 一個 結(jié)構化數(shù)據(jù)
- 消息對象用 修飾符 message 修飾
- 消息對象 含有 字段:消息對象(Message)里的 字段 = 結(jié)構化數(shù)據(jù) 里的成員變量
特別注意:
a. 加入:在一個 .proto文件 中可定義多個 消息對象
- 應用場景:盡可能將與 某一消息類型 相應的響應消息格式 定義到同樣的 .proto文件 中
- 實例:
b. 一個消息對象 里 能夠定義 另外一個消息對象(即嵌套)
message Person {required string name = 1;required int32 id = 2;optional string email = 3;// 該消息類型 定義在 Person消息類型的內(nèi)部 // 即Person消息類型 是 PhoneNumber消息類型的父消息類型message PhoneNumber {required string number = 1;} }<-- 多重嵌套 --> message Outer { // Level 0message MiddleAA { // Level 1message Inner { // Level 2required int64 ival = 1;optional bool booly = 2;}} }2. 字段
- 消息對象的字段 組成主要是:字段 = 字段修飾符 + 字段類型 +字段名 +標識號
- 以下將對每一項詳細介紹
a. 字段修飾符
- 作用:設置該字段解析時的規(guī)則
- 詳細類型例如以下:
b. 字段類型
字段類型主要有 三 類:
- 基本數(shù)據(jù) 類型
- 枚舉 類型
- 消息對象 類型
1. 基本數(shù)據(jù)類型
.proto基本數(shù)據(jù)類型 相應于 各平臺的基本數(shù)據(jù)類型例如以下:
2. 枚舉類型
- 作用:為字段指定一個 可能取值的字段集合
該字段僅僅能從 該指定的字段集合里 取值 - 說明:如以下樣例,電話號碼 可能是手機號、家庭電話號或工作電話號的當中一個。那么就將PhoneType定義為枚舉類型,并將加入電話的集合( MOBILE、 HOME、WORK)
額外說明
當對一個 使用了枚舉類型的.proto文件 使用 Protocol Buffer編譯器編譯時,生成的代碼文件里:
- 對 Java 或 C++來說。將有一個相應的 enum 文件
- 對 Python 來說。有一個特殊的EnumDescriptor 類
被用來在執(zhí)行時生成的類中創(chuàng)建一系列的整型值符號常量(symbolic constants)
3. 消息對象 類型
一個消息對象 能夠?qū)?其它消息對象類型 用作字段類型。情況例如以下:
3.1 使用同一個 .proto 文件里的消息類型
a. 使用 內(nèi)部消息類型
目的:先在 消息類型 中定義 其它消息類型 。然后再使用
即嵌套,須要 用作字段類型的 消息類型 定義在 該消息類型里
實例:
b. 使用 外部消息類型
即外部重用。須要 用作字段類型的消息類型 定義在 該消息類型外部
message Person {required string name = 1;required int32 id = 2;optional string email = 3; }message AddressBook {repeated Person person = 1;// 直接使用了 Person消息類型作為消息字段 }c. 使用 外部消息的內(nèi)部消息類型
message Person {required string name = 1;required int32 id = 2;optional string email = 3;// PhoneNumber消息類型 是 Person消息類型的內(nèi)部消息類型message PhoneNumber {required string number = 1;optional PhoneType type = 2 [default = HOME];} }// 若父消息類型外部的消息類型須要重用該內(nèi)部消息類型 // 須要以 Parent.Type 的形式去使用 // Parent = 須要使用消息類型的父消息類型。Type = 須要使用的消息類型// PhoneNumber父消息類型Person 的外部 OtherMessage消息類型 須要使用 PhoneNumber消息類型 message OtherMessage {optional Person.PhoneNumber phonenumber = 1; // 以 Parent.Type = Person.PhoneNumber 的形式去使用}3.2 使用不同 .proto 文件里的消息類型
- 目的:須要在 A.proto文件 使用 B.proto文件里的消息類型
- 解決方式:在 A.proto文件 通過導入( import) B.proto文件里來使用 B.proto文件 里的消息類型
當然,在使用 不同 .proto 文件里的消息類型 時 也會存在想 使用同一個 .proto 文件消息類型的情況,但使用都是一樣,此處不作過多描寫敘述。
3.3 將 消息對象類型 用在 RPC(遠程方法調(diào)用)系統(tǒng)
- 解決方式:在 .proto 文件里定義一個 RPC 服務接口,Protocol Buffer編譯器會依據(jù)所選擇的不同語言平臺 生成服務接口代碼
- 由于使用得不多,此處不作過多描寫敘述。詳細請看該文檔
c. 字段名
該字段的名稱,此處不作過多描寫敘述。
d. 標識號
作用:通過二進制格式唯一標識每一個字段
- 一旦開始使用就不能夠再改變
- 標識號使用范圍:[1,2的29次方 - 1]
- 不可使用 [19000-19999] 標識號, 由于 Protobuf 協(xié)議實現(xiàn)中對這些標識號進行了預留。假若使用,則會報錯
編碼占有內(nèi)存規(guī)則:
每一個字段在進行編碼時都會占用內(nèi)存,而 占用內(nèi)存大小 取決于 標識號:- 范圍 [1,15] 標識號的字段 在編碼時占用1個字節(jié)。
- 范圍 [16,2047] 標識號的字段 在編碼時占用2個字節(jié)
使用建議
- 為頻繁出現(xiàn)的 消息字段 保留 [1,15] 的標識號
- 為將來有可能加入的、頻繁出現(xiàn)的 消息字段預留 [1,15] 標識號
關于 字段 的高級使用方法
1. 更新消息對象 的字段
- 目的:為了滿足新需求,須要更新 消息類型 而不破壞已有消息類型代碼
即新、老版本號須要兼容
- 更新字段時,須要符合下列規(guī)則:
2. 擴展消息對象 的字段
- 作用:使得其它人能夠在自己的 .proto 文件里為 該消息對象 聲明新的字段而不必去編輯原始文件
- 注:擴展 能夠是消息類型也能夠是字段類型
- 以下以 擴展 消息類型 為例
A.proto message Request { …extensions 100 to 199;// 將一個范圍內(nèi)的標識號 聲明為 可被第三方擴展所用// 在消息Request中。范圍 [100,199] 的標識號被保留為擴展用// 假設標識號須要非常大的數(shù)量時。能夠?qū)⒖蓴U展標符號的范圍擴大至max// 當中max是2的29次方 - 1(536,870,911)。 message Request { extensions 1000 to max; // 注:請避開[19000-19999] 的標識號。由于已被Protocol Buffers實現(xiàn)中預留 }
如今,其它人 就能夠在自己的 .proto文件里 加入新字段到Request里。例如以下:
B.proto
extend Request {optional int32 bar = 126;// 加入字段的 標識號必須要在指定的范圍內(nèi)// 消息Request 如今有一個名為 bar 的 optional int32 字段// 當Request消息被編碼時,數(shù)據(jù)的傳輸格式與在Request里定義新字段的效果是全然一樣的// 注:在同一個消息類型中一定要確保不會擴展新增同樣的標識號。否則會導致數(shù)據(jù)不一致;能夠通過為新項目定義一個可擴展標識號規(guī)則來防止該情況的發(fā)生 }- 要訪問 擴展字段 的方法與 訪問普通的字段 不同:使用專門的擴展訪問函數(shù)
- 實例:
嵌套的擴展
能夠在還有一個 消息對象里 聲明擴展,如:
message Carson {extend Request {optional int32 bar = 126;}… }// 訪問此擴展的C++代碼: Request request; request.SetExtension(Baz::bar, 15);- 對于嵌套的使用,一般的做法是:在擴展的字段類型的范圍內(nèi)定義該擴展
- 實例:一個 Request 消息對象須要擴展(擴展的字段類型是Car 消息類型)。那么,該擴展就定義在 Car消息類型 里:
- 至此,Protoco Buffer的語法已經(jīng)解說完畢
- 關于怎樣依據(jù)需求 通過Protoco Buffer語法 去構建 數(shù)據(jù)結(jié)構 相信大家已經(jīng)非常熟悉了。
- 在將 .proto文件保存后,進入下一個步驟
步驟2:通過 Protocol Buffer 編譯器 編譯 .proto 文件
- 作用:將 .proto 文件 轉(zhuǎn)換成 相應平臺的代碼文件
Protoco Buffer提供 C++、Java、Python 三種開發(fā)語言的 API
- 詳細生成文件與平臺有關:
- 編譯指令說明
- 詳細實例
在指定的文件夾能看到一個Demo的包文件(含 java類文件)
編譯功能的拓展
a. 使用Android Studio插件進行編譯
- 需求場景:每次手動執(zhí)行 Protocol Buffer 編譯器將 .proto 文件轉(zhuǎn)換為 Java 文件 操作不方便
- 解決方式:使用 Android Studio的 gradle 插件 protobuf-gradle-plugin,以便于在項目編譯時 自己主動執(zhí)行 Protocol Buffers 編譯器
關于protobuf-gradle-plugin插件有興趣的讀者可自行了解。但個人還是建議使用 命令行,畢竟太過折騰插件不是必需
b. 動態(tài)編譯
- 需求場景:某些情況下。人們無法預先知道 .proto 文件,他們須要動態(tài)處理一些未知的 .proto 文件
如一個通用的消息轉(zhuǎn)發(fā)中間件。它無法預先知道須要處理什么類型的數(shù)據(jù)結(jié)構消息
- 解決方式:動態(tài)編譯.proto文件
由于使用得不多,此處不作過多描寫敘述。詳細請看官方文檔
c. 編寫新的 .proto 編譯器
- 需求場景: Protocol Buffer 僅支持 C++、java 和 Python 三種開發(fā)語言。一旦超出該三種開發(fā)語言,Protocol Buffer將無法使用
- 解決方式:使用 Protocol Buffer 的 Compiler 包 開發(fā)出支持其它語言的新的.proto編譯器
由于使用得不多,此處不作過多描寫敘述,詳細請看官方文檔
5.3 應用到詳細平臺(Android平臺)
- 最終到了應用到詳細平臺項目中的步驟了。
此處以 Android平臺 為例
- 詳細過程例如以下:
步驟1:將生成的 代碼文件 放入到項目中
- 對于Android(Java)平臺。即將編譯.proto文件生成的Java包文件 整個拷貝到 Android 項目中
- 放置路徑: app/src/main/java的 文件夾里
步驟2:在 Gradle 加入 Protocol Buffer 版本號依賴
compile 'com.google.protobuf:protobuf-java:2.6.1' // 注:protobuf-java的版本號 一定要和 安裝protocobuffer的版本號 一致步驟3:詳細在Android項目中使用
3.1 消息對象類介紹
通過.proto文件 轉(zhuǎn)換的 Java源碼 = Protocol Buffer 類 + 消息對象類(含Builder內(nèi)部類)
消息對象類 是 Protocol Buffer 類的內(nèi)部類
由于最經(jīng)常使用的都是 消息對象類 和其內(nèi)部類Builder類 的方法&成員變量,所以此處主要解說這兩者。
3.1.1 消息對象類(Message類)
- 消息對象類 類通過 二進制數(shù)組 寫 和 讀 消息類型
- 使用方法包括:
經(jīng)常使用的如上,很多其它請看官方文檔
3.1.2 Builder類
作用:創(chuàng)建 消息構造器 & 設置/ 獲取消息對象的字段值 & 創(chuàng)建 消息類 實例
屬于 消息對象類 的內(nèi)部類
a. 創(chuàng)建 消息構造器
Demo.Person.Builder person = Person.newBuilder();b. 設置/ 獲取 消息對象的字段值 詳細方法例如以下:
// 標準的JavaBeans風格:含getters和setters // required string name = 1; public boolean hasName();// 假設字段被設置,則返回true public java.lang.String getName(); public Builder setName(String value); public Builder clearName(); // 將字段設置回它的空狀態(tài)// required int32 id = 2; public boolean hasId(); public int getId(); public Builder setId(int value); public Builder clearId();// optional string email = 3; public boolean hasEmail(); public String getEmail(); public Builder setEmail(String value); public Builder clearEmail();// repeated .tutorial.Person.PhoneNumber phone = 4; // 反復(repeated)字段有一些額外方法 public List<PhoneNumber> getPhoneList(); public int getPhoneCount(); // 列表大小的速記 // 作用:通過索引獲取和設置列表的特定元素的getters和setterspublic PhoneNumber getPhone(int index); public Builder setPhone(int index, PhoneNumber value);public Builder addPhone(PhoneNumber value); // 將新元素加入到列表的末尾public Builder addAllPhone(Iterable<PhoneNumber> value); // 將一個裝滿元素的整個容器加入到列表中 public Builder clearPhone();public Builder isInitialized() // 檢查全部 required 字段 是否都已經(jīng)被設置public Builder toString() : // 返回一個人類可讀的消息表示(用于調(diào)試)public Builder mergeFrom(Message other) // 將 其它內(nèi)容 合并到這個消息中,覆寫單數(shù)的字段,附接反復的。public Builder clear() // 清空全部的元素為空狀態(tài)。3.2 詳細使用
使用過程例如以下:
步驟1:通過 消息類的內(nèi)部類Builder類 構造 消息構造器
步驟2:通過 消息構造器 設置 消息字段的值
步驟3:通過 消息構造器 創(chuàng)建 消息類 對象
步驟4:序列化 / 反序列化 消息詳細使用例如以下:(凝視非常清晰)
Demo 地址
Carson_Ho的Github :https://github.com/Carson-Ho/ProtocolBuffer
高級功能
貼心的Google還提供將Protocol Buff 編碼方式 轉(zhuǎn)化為 其它編碼方式,如 Json、XML等等
即將 Protocol Buff 對象 轉(zhuǎn)化為其它編碼方式的數(shù)據(jù)存儲對象
以下展示的是 將 Protocol Buff 對象 轉(zhuǎn)化為 Json對象
6. 總結(jié)
- 在 數(shù)據(jù)傳輸量較大的需求場景下,Protocol Buffer比XML、Json 更小、更快、使用 & 維護更簡單!
- 以下用 一張圖 總結(jié)在 Android平臺中使用 Protocol Buffer 的整個步驟流程:
- 看完本文,你應該會非常好奇為什么Protocol Buffer 的優(yōu)勢這么大:為什么序列化后的數(shù)據(jù)包比XML、Json更小、傳輸速度更快?
- 下一篇文章我將對Protocol Buffer 進行源碼分析。有興趣能夠繼續(xù)關注我的CSDN博客!
請幫頂或評論點贊!
由于你的鼓舞是我寫作的最大動力!
總結(jié)
以上是生活随笔為你收集整理的快来看看Google出品的Protocol Buffer,别仅仅会用Json和XML了的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 利用ImageMagick命令执行漏洞拿
- 下一篇: IDC:全球物联网支出将在2019年达到