Protocol Buffers工作原理_網頁設計公司

網頁設計一頭霧水該從何著手呢? 台北網頁設計公司幫您輕鬆架站!

透過資料庫的網站架設建置,建立公司的形象或購物系統,並提供最人性化的使用介面,讓使用者能即時接收到相關的資訊

這裏記錄一下學習與使用Protocol Buffer的筆記,優點缺點如何使用這裏不再敘述,重點關注與理解Protocol Buffers的工作原理,其大概實現。

我們經常使用Protocol Buffer進行序列化與反序列化。理解Protocol Buffer的工作原理,就要理解序列化與反序列化。

  • 序列化:將數據結構或對象轉換為二進制串的過程;
  • 反序列化:序列化的逆過程;

如何實現呢?核心有兩點:編碼 + 存儲。數據在計算機間通過網絡進行傳輸時,傳輸的是比特流,只有0和1,並沒有你所定義的各種類對象等,你如果想將一個類對象傳輸到對方,怎麼辦呢?字符是用過ASCII碼編碼的,這裏也可以設計一套編碼方案來對類似類對象這種數據進行編碼,只要對方收到后能正確的解碼就可以了。編碼后還要確定編碼后的數據存儲方式,這樣字節流才是有意義的字節流,這樣才能知道讀取的字節流有什麼含義,代表什麼。

好了,我們看一下Protocol Buffer是如何編碼和存儲的。

Protocol Buffer是如何編碼的

Varint編碼

Varint編碼是一種變長的編碼方式,核心思想是對數值越小的数字,用越少的字節數表示,這樣可以減少数字的字節數,進行數據壓縮。

舉個例子:對int數據類型,一般需要4個字節來表示,而實際上,對與數值較小的数字而言,無需這麼多字節,00000000 00000000 00000000 01111111 | 127,只需要一個字節就能表示,前面3個字節意義不大,浪費了許多空間。

當然,這種編碼並不是所有情況下都會變小,當數值非常大時,所需的字節會增多,但因為大多數情況下數值小的数字遠比數值大的多,所以整體看來,數據是被壓縮了的。

具體的,Varint編碼時,對每個字節的最高位賦予特殊含義:

  • 1:表示後序的字節也是該数字的一部分;
  • 0:表示這是最後一個字節,且剩餘7bit都用來表示数字(所以Varint解碼時,如果讀到最高位為0的字節時,就表示已經是Varing的最後一個字節);

因為每個字節的最高位都被佔用,用來表示特殊的含義,所以,當數值非常大時,原有的字節數就不夠用了,所以編碼時要增加字節數。

可以參考下圖加深理解:

編碼示例:

解碼示例:

還有一個問題:就是負數時怎麼辦?計算機中數值用補碼形式表示和存儲(負數在計算機中往往用最位1來表示負數,0表示正數,負數的補碼最高位為1),那按Varint編碼方式所有的負數都需要增加一個字節表示,這是不能被接受的,解決方法便是下面要講的zigzag編碼。

Zigzag編碼

Zigzag編碼是一種變長的編碼方式。zigzag按絕對值升序排列,將整數hash成遞增的數值序列,哈希函數為h(n)=(n<<1)^(n>>31),對應地long型(64)位的哈希函數為h(n)=(n<<1)^(n>>63)

※想知道最厲害的網頁設計公司嚨底家"!

RWD(響應式網頁設計)是透過瀏覽器的解析度來判斷要給使用者看到的樣貌

n hex h(n) zigzag(hex)
0 00 00 00 00 00 00 00 00 00
-1 ff ff ff ff 00 00 00 01 01
1 00 00 00 01 00 00 00 02 02
-2 ff ff ff fe 00 00 00 03 03
2 00 00 00 02 00 00 00 04 04

可以看到,zigzag編碼將正數和負數重新映射為遞增的無符號數,其主要目的就是使絕對值小的數值映射為小的無符號數,已方便後序壓縮字節。

zigzag編碼還有很多細節,比如為了保證編碼的唯一可譯性,需對哈希值進行前綴碼編碼,這裏不再細述。

解碼為編碼的逆,首先將zigzag編碼還原成哈希值,然後用哈希函數h的逆h’(n)=(n>>>1)^-(n&1)得到原始整數值。

這裏只重點講Varint編碼和Zigzag編碼,像string、double等類型這裏不再描述,可參考文後的參考文檔。

T-L-V的數據存儲方式

T:tag,L:length,V:value。以標識-長度-字段值表示單個數據,最終將所有數據拼接成一個字節流,從而實現數據存儲的功能。

其中Length可選存儲,如存儲Varint編碼數據就不需要存儲Length,因為可根據每個字節最高bit位來判斷這個字節是不是該數據段的最後一個字節。

這裏重點說一下Tag。Tag用來標識字段,通過Tag能獲知這段字節流是屬於什麼類型數據的,其定義為:Tag = (field_number << 3) | wire_type
。這樣,解包時就可根據tag將value對應消息中的字段。

Tag佔用一個字節的長度(如果標識號超過了16,則多佔用一個字節的位置,原因是field_number左移了3位,編碼方式為Varint&Zigzag,編碼的時候一個字節不夠用了)。

message ChannelDataAck {
    bytes uuid = 1;     //這裏的1 、2就是field_number
    uint32 result = 2;  
}

Tag(字段標識號)在序列化和反序列化過程中非常重要。舉一個應用中非常常見的例子,在需要對原有結構進行增減字段的時候,同樣一個結構體定義,新版本代碼中對其增加了一個字段,那當新版本代碼序列化后給原有舊版本反序列化解析的時候,因為舊的沒有那個新增的字段,所以在解析時只解析自己有的字段,沒有的不進行解析,這樣舊的代碼依舊能從新字節流中解析出舊數據結構。那舊的數據結構的數據解析為新數據結構時,因為沒有新字段的數據,解析為新數據時該字段置為默認值。這樣就能保證兼容性,對協議升級較為友好。

可以看到通過T-L-V的數據存儲方式,能夠較好的解決字段不完全匹配時的如何解析的問題。

序列化 & 反序列化過程

序列化過程如下:

  1. 判斷每個字段是否有設置值,有值才進行編碼.
  2. 根據字段標識號&數據類型將字段值通過不同的編碼方式進行編碼.

反序列化過程如下:

  1. 解析從輸入流讀入的二進制字節數據流.
  2. 將解析出來的數據按照指定的格式讀取到C++、Rust等對應的結構類型中.

到這裏,基本把Protocol Buffers的工作原理簡單梳理了一遍,其他技術細節待以後再深究。

本站聲明:網站內容來源於博客園,如有侵權,請聯繫我們,我們將及時處理

網頁設計一頭霧水該從何著手呢? 台北網頁設計公司幫您輕鬆架站!

當全世界的人們隨著網路時代而改變向上時您還停留在『網站美醜不重要』的舊有思維嗎?機會是留給努力改變現況的人們,別再浪費一分一秒可以接觸商機的寶貴時間!

您可能也會喜歡…