Ⅰ protobuf 怎麼查看版本
protobuf版本需要在protobuf程序中查看。
在protobuf程序中查看版本步驟如下所示:
1、點擊打開計算機,進入分區列表。
Ⅱ 怎樣在protobuf中添加消息長度和類型
編譯後protobuf形成對應的文件,加入工程,創建你的消息對象,給裡面的成員賦值,然後將這個對象轉化為位元組流,用socket函數直接write出去即可。
Ⅲ 了解一下ProtoBuf
我們在進行網路通信調用的時候,總是需要將內存的數據塊經過序列化,轉換成為一種可以通過網路流進行傳輸的格式。而這種格式在經過了傳輸之後再經過序列化,能還原成我們預想中的數據結構。
那麼我們對於這種用於中間網路傳輸的數據格式就有一定的要求。首先它可以准確地描述數據內容,在此基礎上我們則希望它盡量的小。
最開始流行起來的是XML,可擴展標記語言。由於它可以用來標記數據、定義數據類型,所以用戶可以自己定義數據自己的語言,從而讓對不同的數據結構化成統一的格式稱為了可能。
而另外一個我們熟知的則是jsON(javaScript Object Notation, JS 對象簡譜)。盡管JSON中缺少了XML中的標簽屬性等描述方式,但是足夠簡介和清晰的層次結構使得其成為了必XML更受歡迎的數據交換格式。
同一份數據顯然JSON的數據量比XML所使用的空間更少。那麼空間省略在哪裡呢?一方面是json使用更簡單的字元來定義數據間的關聯關系;另一方面是JSON減少了對數據類型的描述。但是丟少的數據類型再哪裡呢?
以Java中的 OpenFeign 舉例,JSON中缺少的類型定義被定義道程序中的介面中了。當進行序列化與反序列化時,JSON格式並不記錄數據的類型,具體的數據類型在序列化方與反序列化方通過事先約定的介面來進行定義。這樣就減少了信息傳輸過程中的信息量,從而讓數據得以壓縮。
但是JSON由於沒有定義數據類型,所以在傳輸的過程中實際上就都是文本流,那麼這種方法還可以進一步壓縮嗎?
結合上文的討論,我們先說結論:方法是有的,並寫當前的實現方式是ProtoBuf。但在此之前我們先來了解一下ProtoBuf。
我們可以先看看官方給出的定義與描述:
同樣的,ProtoBuf也是一種支持序列化反序列化的方法,並且他具有很多優點:
實際上,ProtoBuf提供了一種通用的數據描述方式,這種定義數據的方式是通用的,就如同JSON或者XML一樣。
接下來我們來來回答本節一開始的問題,針對JSON來說,ProtoBuf是如何將體積變得更小的呢?答案很簡單,就是為數據序列化反序列化提供更多的先驗知識。
本文暫不過度深入ProtoBuf原理,但是可以通過一張圖來進行簡要說明():
ProtoBuf中的數據是按順序進行排列,而整體的結構為若干個field,每一個field中由 Tag-[Length]-Value 組成。Length是可選的,而是否存在Length是通過Tag的類型來決定的。也就是說如果是指定的類型,比如int64,那我們就可以知道Value的長度,也就不用在依靠Length來對其空間進行描述(redis中的壓縮列表也是這個思想)。
那麼field應該對應的是什麼欄位呢?這個則是在序列化與反序列化時在ProtoBuf的服務端與客戶端之間進行預先定義的。而因為提前定義了field的類型、排序,所以field本身可以不用對欄位名、欄位位置進行描述,只需要根據欄位類型選用合適的二進制序列化方法,將欄位本身的value值進行序列化傳輸即可。
稍微總結一下:
ProtoBuf通過對傳輸欄位的名稱、順序進行預定義,從而在傳輸結構中只需要順序的記錄每個欄位的類型標簽和二進制值。
盡管上文和官方中都是以XML或者JSON來對ProtoBuf進行對比。但是因為ProtoBuf本身就是二進制序列化方式,所以從壓縮比上比較感覺有點欺負人。
對應的在Java中二進制常用的序列化器有Kryo和Hessian。但事實上,由於Kryo和Hessian中都需要對Java類名和欄位信息進行存儲。而ProtoBuf則只有Tag-Length-Value的數據對,且Value更是有針對性的特殊編碼,所以空間佔用小的很多。
Kryo是專門針對Java進行優化了的。所以在使用的便捷性上來說Kryo則更加方便。但ProtoBuf是跨平台的,且由於進行了欄位的順序定義,所以似的ProtoBuf定義後的介面是可以向前兼容的(只向後追加欄位),而這種優勢是Kryo所沒有的。
ProtoBuf是跨語言的,使用ProtoBuf的第一步是先定一個 proto 文件 ,而由於ProtoBuf 2和3語言版本的不同,其定義格式會有所不同,具體的細節還是得參考官方文檔:https://developers.google.cn/protocol-buffers/docs/proto3
對於ProtoBuf 3 的定義文檔我們可以按如下方法定義:
其中message關鍵字是定義的文件名,而 string、int32則是預定的欄位類型,repeated則是描述欄位為可重復任意多次的欄位。
ProtoBuf通過這種形式的文件定義了傳輸信息的文件結構。
但是之前小節中我們知道了ProtoBuf是通過 Tag-[Length]-Value 組成的數據組來進行信息傳輸的,那麼proto文件中定義的內容如何轉換為實際傳輸的對象呢?
ProtoBuf的做法是,為每一種語言提供一個生成器protoc。通過使用protoc則可以根據.proto文件生成為一組java文件。對應的官方語法演示樣例為:
官方的生成參考為:https://developers.google.com/protocol-buffers/docs/reference/java-generated
生成後的java文件將提供對應的實體以及數據的構造方法等文件,從而支持後續的使用。
需要注意的是,ProtoBuf是本質上是序列化方法,具體是通過Spring Cloud 的OpenFeign進行介面調用,還是通過grpc進行介面調用,都是可以的。
本文對ProtoBuff進行了概念的整理,並沒有對每個細節都進行深入的梳理,可以當作概念科普來進行閱讀。
Ⅳ ProtocolBuffer淺析
ProtocolBuffer是google 定義的一種數據交換的格式,它獨立於語言,獨立於平台。google 提供了多種語言的實現:java、c#、c++、go 和 python,每一種實現都包含了相應語言的編譯器以及庫文件。ProtocolBuffer類似於xml、json,不過它更小、更快、也更簡單。
目前使用最廣泛的數據傳輸協議為JSON,JSON是一種輕量級的數據交換格式而且層次和結構比較簡單和清晰,這里主要對比一下Protocol Buffer和JSON的對比,給出優勢和劣勢:
優勢
劣勢
實際數據對比
Protocol Buffer的使用流程總體可以分為三步,如下圖所示:
google推薦在Android項目中使用lite版,lite版本生成的java文件更加輕量,其配置如下:
首先創建一個.proto文件,並且在文件中聲明如下內容:
在整個proto文件中數據類型分為基本類型和結構類型,其中結構類型主要為:
下面分別介紹一下不同結構的作用及規定:
message表示一個結構,類似於java中類,一個proto文件中可以聲明多個message結構:
message可以引用不同proto文件中的message,只要在proto文件中的最上面聲明import即可,如下所示:
enum使用很簡單,直接在message中聲明enum結構體並且將屬性聲明為對應的enum即可:
在proto3中,enum第一個值必須為0,主要是為了和基礎類型的默認值保持一致
map是proto3新加的,使用也很簡單:
如下
repeated修飾的屬性類似於jsonArray,也類似於java中的List,該修飾符在格式正確的消息中可以重復任意次(包括0次)
日常開發過程中,由於需求的變更,往往需要增加欄位,這就涉及到欄位的擴充,欄位擴充需要達到一個目的: 兼容
所以Protocol Buffer在欄位擴充中定義了如下規則:
只要記住上述規則,就能完成欄位擴充且老版本也能兼容
Protocol Buffer 更快更小的主要原因如下:
上面這個例子中,在序列化時,"name" 、"count"的key值不會參與,由編號1、2代替,這樣在反序列化的時候直接通過編號找到對應的key就可以。需要注意的是編號一旦確定就不可以更改,服務端和客戶端通過proto通信的時候需要提前定義號數據格式。
其中Length不一定有,依據Tag確定,例如int類型的數據就只有Tag-Value,string類型的數據就必須是Tag-Length-Value。
Protocol Buffer定義了如下的數據類型,其中部分數據類型已經不再使用:
上面已經介紹了Protocol Buffer的數據結構及Tag的類型,但是Tag塊並不是只表示數據類型,其中數據編號也在Tag塊中,Tag的生成規則如下:
其中Tag塊的後3位表示數據類型,其他位表示數據編號
Java中整數類型的長度都是確定的,如int類型的長度為4個位元組,可表示的整數范圍為-2 31——2 31-1,但是實際開發中用到的數字均比較小,會造成位元組浪費,可變長度編碼就能很好的解決這個問題,可變長度編碼規則如下:
舉個例子:
其中第一個位元組由於最高位為1,則後面的位元組也是前面的數據的一部分,第二個位元組最高位為0,則表示數據計算終止,由於Protocol Buffer是低位在前,整體的轉換過程如下:
10000001 00000011 ——> 00000110000001 表示的10進制數為:2^0 + 2^7 + 2^8 = 385 通過上面的例子可以知道一個位元組表示的數的范圍0-128,上面介紹的Tag生成演算法中由於後3位表示數據類型,所以Tag中1-15編號只佔用1個位元組,所以確保編號中1-15為常用的,減少數據大小。
可變長度編碼唯一的缺點就是當數很大的時候int32需要佔用5個位元組,但是從統計學角度來說,一般不會有這么大的數.
上面介紹了Protocol Buffer的原理,現在通過實例來展示分析過程,我們定義的proto文件如下:
其序列化後的位元組數據如下:
前面介紹過Protocol Buffer的 數據結構為TLV,其中L不是必須的,根據T的類型來確定 先看下第一個位元組:
這里位元組最高位為0,所以該Tag就用這一個位元組表示,其中後3位表示類型,前面表示欄位編號,所以:
這里位元組最高位為0,所以該Tag就用這一個位元組表示,其中後3位表示類型,前面表示欄位編號,所以: file_num = 0001 = 1 type = 010 = 2 上面介紹過type=2,則後面有Length,按照可變長度編碼規則,知道表示長度的位元組為:
所以Length=4,則value的長度是4個位元組,直接取出後面4個位元組:
這4個位元組對應的就是test 再看下一組:
由上面的Tag知道: file_num=2 type=0 前面介紹過type=0,後面沒有Length,直接就是value,
value=1,通過上面的解析可以知道
上面介紹了Protocol Buffer的原理,解釋了為什麼Protocol Buffer更快,更小,這里再總結一下:
參考資料:
proto3官網指南: https://developers.google.com/protocol-buffers/docs/proto3
protobuf-gradle-plugin: https://github.com/google/protobuf-gradle-plugin
博客: https://juejin.im/post/5dcbf630e51d451bfe5bb21b