造輪子系列之Protobuf

作為一個程序猿,對造輪子這事情可以說是情有獨鐘,幾乎程序猿內心都存在一個夢想是去將開源的技術都實現一遍,所有從本篇開始,我會開一個造輪子系列。

前言

首先,看看這個,想必大家對下面這種簡歷看得比較多了吧?

精通JAVA,Python,熟練掌握C++

精通Redis,Memcached,Mysql

精通Nginx配置,模塊開發

精通Kafka,ActiveMQ 等消息隊列

精通常用數據結構和算法

精通網絡編程,多線程編程技術,高性能服務器技術

精通tcp/ip協議棧,熟悉內核網絡子系統代碼

精通nginx代碼及模塊開發

上面每一條都涉及好多輪子,每一個都是精通,如果真能做到。那這個人可以說是碼農中的戰斗機。

那我們現在目標就是去做這個戰斗機。而這個方法,就是自己去造輪子,造的目的不是為了在項目中使用自己造的輪子,而是為了去了解輪子的構造,然后自己動手去體會造輪子的過程。

后端的輪子們

說起后端的輪子們,大家都可以說出一大串來,我們大致來數一數啊。

抗在最前面的:LVS,F5,HAProxy這類負載均衡

接下來有Nginx,Apache,Lighttpd這類Http服務

http服務后則是各種容器,部署著我們的業務邏輯

存儲這邊有Redis,Memcached這一類KV存儲器和緩存系統

如果是多機部署,肯定還有Kafka,ActiveMQ這種負責解耦的消息隊列

為了實現集群通信,肯定少不了Thrift這種RPC框架和Protobuf這種序列化技術

再高端點,到了分布式領域了,就是更多的輪子了。。zookeeper、raft等等

還有大數據系列hadoop。spark。。。。。

本文先開始我們的第一個輪子,服務器通信需要用的數據序列化反序列技術:protobuf。

基礎輪子:protobuf

講基礎前,先附上一張極客時間中的一個技術需要從哪些角度來講的圖片,本文也會盡可能從這些個方面來講。

應用角度

1. 問題:”干什么用“

2. 技術規范:”怎么用“

3. 最佳實踐:”怎么能用好“

4. 市場應用趨勢:“誰用,用在哪”

設計角度

1. 目標:“做到什么”

2. 實現原理:“怎么做到”

3. 優劣局限:“做得怎么樣”

4. 演進趨勢:“未來如何”

正文

Protocol buffers

從應用角度看protobuf是干什么用的?

序列化數據用的?什么時候需要序列化?當數據需要存儲或者網絡傳輸的時候。為什么呢?

在存儲或者傳輸的時候,我們能看到都是一些二進制數據,即010101……的bit。

假設我們看的一個對象是:

Struct myData {

Int a;

Int b;

}

data = myData {

a:1,

b:2,

}

那我們在網絡上收到是一個字節流,我們為了能夠從字節流中恢復出數據 data,我們要做的工作是:

正確識別出data在字節流中開始和結束的位置

識別出a的值,識別出b的值

一個可能的字節流協議就是:

剛開始是8bit標明后續數據是哪個結構,然后是兩個4字節表示a和b。

注意!!!!!上面做出上面這個假設,有幾點是我們默認的:

我們認為字節流開始先是8bit標明是哪個數據結構,此處是myData(ps:不同結構之間編號不同)

最多能夠支持2^8種結構

通訊雙方都需要拿到myData的定義文件

以下是一個上面實現的示例代碼:

可以看到在go中很容易就實現了我們的一個數據結構的序列化反序列化。

設計角度,做到什么?

上面我們只是實現了一個最簡單的實現了一個序列化方法,下面我們來看如果要實現一個生產環境中的序列化協議,需要做到哪幾點。

通用性:語言、平臺無關

高性能:序列化和反序列化都要快

高壓縮:序列化后數據盡可能小,小就意味著網絡傳輸數據少

兼容性:數據結構改變了,也能夠支持新老版本

下面我們帶著這些目標來從應用角度來看下”怎么用“

這個就是官方文檔了https://developers.google.com/protocol-buffers/docs/gotutorial,里面有詳細的說明,另外我自己給了一份使用示例:https://github.com/zhuanxuhit/go-in-practice/tree/master/wheel/protobuf/v2

講完使用下面就是設計角度:如何做到的?

首先我們看前文我們自己實現的簡易序列化、反序列方法,我們對每個結構進行編碼,然后在頭部寫上該結構是啥,然后后面就是結構中每個字段的具體值,接著我們寫了序列化器的目標是:簡易、高效、兼容,下面我們從這幾個方面來看protobuf有什么改進的地方。

先來個小插曲,protobuf全稱是Protocol buffers,其中buffers點名了使用上非常重要的一個點,即我們在反序列化的一段二進制數據的時候,我們要將其先讀入到buffer中,然后再識別出單個數據結構的開頭和結尾,最后才能正確的反序列化出來。

前面我們設計的時候,還在頭部對數據結構進行了編碼,那為了能夠做到更高效,數據更小,我們是否可以把這個頭也去掉呢?

當然是可以的,于是我們的結構就變為了只有對應的字段值了,這么做的一個前提是:!!我們必須清楚知道我們識別出來二進制數據,其對應的具體是哪個數據結構。!!

現在我們去掉了結構描述,那怎么能夠做到更小呢?譬如同樣是int64,1 和 1<<32沒必要都用8字節來表示,譬如我們可以先對數據類型做一個編碼,然后緊跟著后續使用的bit,再跟著真正的數據。

每個部分分別用幾個bit來表示呢?

數據類型:根據支持的類型進行編碼,如果總共能支持16種類型,那就是4bit

后續有效字節:這個比較難辦,由于我們不確定數據大小,我們就無法固定bit來表示。

那一個解決方法就是:我們去掉有效字節的字段,我們把這個是否有更多數據信息編碼進數據本身中,示意圖如下:

解決了編碼int類型的字段后,如果遇到string類型呢?這種類型首先也是數據類型描述,接著應該要是編碼后續有效字節,這是一個int,這可以采用上面的方法來編碼,再跟著就是有效數據了。

以上就是protobuf在編碼數據時采用編碼方式的主要思想,具體可以看https://developers.google.com/protocol-buffers/docs/encoding。

目前protobuf支持的數據類型

上面有個主意的對于有符號數,我們要單獨處理下,因為有符號數的最高位是通過0,1來表示正負的,但是上面編碼中最高位卻用來表示是否有后續數據了,所以我們要通過ZigZag 編碼將有符號轉換為無符號。

原理很簡單,就是通過下面的編碼方式:

解決了編碼后,我們來看最后一個問題:兼容性。

如果我們改變了數據結構:新增或者刪除了字段怎么辦???

這個也好解決,那我們就給所有字段加上編號,通過字段來表示這個數據是結構體中哪個字段,protobuf在設計上編碼方式如下:

圖片來自:高效的數據壓縮編碼方式 Protobuf

從上圖中tag的編碼,我們可以發現,如果field_num > 16的話,tag編碼出來會使用超過1字節,所有對于我們經常使用的字段,建議將其編碼到0-15,減少tag位數。

實現原理小結

下面我們對上面介紹的實現原理做個小結

高效:變長編碼,非自描述

兼容:對filed進行編碼

下面我們再從應用角度講下 protobuf 的最佳實踐和市場應用趨勢。

protobuf剛開始設計出來主要是為了解決接口兼容性問題,目前是主要用在內部服務之間RPC調用和傳遞數據,目前時候用protobuf作為序列化的rpc框架有gRPC,這也是后續我們會介紹的。

最后我們從設計角度來看下protobuf的優劣局限和演進趨勢。

優點

protobuf最大的優點就是前后兼容性,已經部署的使用老數據格式的服務,即使接口升級了也可以繼續使用,然后就是性能,當然是快了,具體可以看 序列化 / 反序列化性能

缺點

相比較json來說,可讀性差,特別是在調試階段,相比較json我們無法清晰的知道輸入和輸出。

最后

總結下本文

Protobuf設計之初主要是為了解決兼容性問題,實現上是對每個字段進行編號,當遇到不存在的字段時,則忽略掉。

Protobuf為了能夠做到高性能,在編碼時采用了Tag - Value (Tag - Length - Value)的方式,使序列化后的數據更緊湊

Protobuf為了能夠做到高性能,丟棄了自描述信息,即我們只拿到數據,而沒有拿到proto文件,我們是無法反序列數據的

Protobuf提供了一套編譯工具,能夠生成不同語言的數據序列化、反序列化方法,極大的提高了易用性

預告

下一篇我們會介紹grpc,來看下rpc框架中是怎么使用protobuf的。

參考

高效的數據壓縮編碼方式 Protobuf

官方文檔

?著作權歸作者所有,轉載或內容合作請聯系作者
平臺聲明:文章內容(如有圖片或視頻亦包括在內)由作者上傳并發布,文章內容僅代表作者本人觀點,簡書系信息發布平臺,僅提供信息存儲服務。
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市,隨后出現的幾起案子,更是在濱河造成了極大的恐慌,老刑警劉巖,帶你破解...
    沈念sama閱讀 229,732評論 6 539
  • 序言:濱河連續發生了三起死亡事件,死亡現場離奇詭異,居然都是意外死亡,警方通過查閱死者的電腦和手機,發現死者居然都...
    沈念sama閱讀 99,214評論 3 426
  • 文/潘曉璐 我一進店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人,你說我怎么就攤上這事。” “怎么了?”我有些...
    開封第一講書人閱讀 177,781評論 0 382
  • 文/不壞的土叔 我叫張陵,是天一觀的道長。 經常有香客問我,道長,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 63,588評論 1 316
  • 正文 為了忘掉前任,我火速辦了婚禮,結果婚禮上,老公的妹妹穿的比我還像新娘。我一直安慰自己,他們只是感情好,可當我...
    茶點故事閱讀 72,315評論 6 410
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著,像睡著了一般。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發上,一...
    開封第一講書人閱讀 55,699評論 1 327
  • 那天,我揣著相機與錄音,去河邊找鬼。 笑死,一個胖子當著我的面吹牛,可吹牛的內容都是我干的。 我是一名探鬼主播,決...
    沈念sama閱讀 43,698評論 3 446
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了?” 一聲冷哼從身側響起,我...
    開封第一講書人閱讀 42,882評論 0 289
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后,有當地人在樹林里發現了一具尸體,經...
    沈念sama閱讀 49,441評論 1 335
  • 正文 獨居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內容為張勛視角 年9月15日...
    茶點故事閱讀 41,189評論 3 356
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發現自己被綠了。 大學時的朋友給我發了我未婚夫和他白月光在一起吃飯的照片。...
    茶點故事閱讀 43,388評論 1 372
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖,靈堂內的尸體忽然破棺而出,到底是詐尸還是另有隱情,我是刑警寧澤,帶...
    沈念sama閱讀 38,933評論 5 363
  • 正文 年R本政府宣布,位于F島的核電站,受9級特大地震影響,放射性物質發生泄漏。R本人自食惡果不足惜,卻給世界環境...
    茶點故事閱讀 44,613評論 3 348
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧,春花似錦、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 35,023評論 0 28
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至,卻和暖如春,著一層夾襖步出監牢的瞬間,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 36,310評論 1 293
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留,地道東北人。 一個月前我還...
    沈念sama閱讀 52,112評論 3 398
  • 正文 我出身青樓,卻偏偏與公主長得像,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當晚...
    茶點故事閱讀 48,334評論 2 377

推薦閱讀更多精彩內容