做爲一個程序猿,對造輪子這事情能夠說是情有獨鍾,幾乎程序猿心裏都存在一個夢想是去將開源的技術都實現一遍,全部從本篇開始,我會開一個造輪子系列。
首先,看看這個,想必你們對下面這種簡歷看得比較多了吧?
-
精通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。
講基礎前,先附上一張極客時間中的一個技術須要從哪些角度來說的圖片,本文也會盡量從這些個方面來說。
-
應用角度
-
-
問題:」幹什麼用「
-
技術規範:」怎麼用「
-
最佳實踐:」怎麼能用好「
-
市場應用趨勢:「誰用,用在哪」
-
設計角度
-
-
目標:「作到什麼」
-
實現原理:「怎麼作到」
-
優劣侷限:「作得怎麼樣」
-
演進趨勢:「將來如何」
Protocol buffers
應用角度
幹什麼用的?
序列化數據用的?何時須要序列化?當數據須要存儲或者網絡傳輸的時候。爲何呢?
在存儲或者傳輸的時候,咱們能看到都是一些二進制數據,即010101……的bit。
假設咱們看的一個對象是:
Struct myData
{ Int a; Int b;}data = myData { a:1, b:2,}
那咱們在網絡上收到是一個字節流,咱們爲了可以從字節流中恢復出數據 data,咱們要作的工做是:css
-
正確識別出data在字節流中開始和結束的位置
-
識別出a的值,識別出b的值
一個可能的字節流協議就是:
剛開始是8bit標明後續數據是哪一個結構,而後是兩個4字節表示a和b。
注意!!!!!上面作出上面這個假設,有幾點是咱們默認的:
-
咱們認爲字節流開始先是8bit標明是哪一個數據結構,此處是myData(ps:不一樣結構之間編號不一樣)
-
最多可以支持2^8種結構
-
通信雙方都須要拿到myData的定義文件
如下是一個上面實現的示例代碼見GitHub。
能夠看到在go中很容易就實現了咱們的一個數據結構的序列化反序列化。
設計角度
作到什麼?
上面咱們只是實現了一個最簡單的實現了一個序列化方法,下面咱們來看若是要實現一個生產環境中的序列化協議,須要作到哪幾點。
-
通用性:語言、平臺無關
-
高性能:序列化和反序列化都要快
-
高壓縮:序列化後數據儘量小,小就意味着網絡傳輸數據少
-
兼容性:數據結構改變了,也可以支持新老版本
下面咱們帶着這些目標來從應用角度來看下」怎麼用「
講完使用下面就是設計角度:如何作到的?
首先咱們看前文咱們本身實現的簡易序列化、反序列方法,咱們對每一個結構進行編碼,而後在頭部寫上該結構是啥,而後後面就是結構中每一個字段的具體值,接着咱們寫了序列化器的目標是:
簡易、高效、兼容,下面咱們從這幾個方面來看protobuf有什麼改進的地方。
先來個小插曲,protobuf全稱是Protocol buffers,其中buffers點名了使用上很是重要的一個點,即咱們在反序列化的一段二進制數據的時候,咱們要將其先讀入到buffer中,而後再識別出單個數據結構的開頭和結尾,最後才能正確的反序列化出來。
前面咱們設計的時候,還在頭部對數據結構進行了編碼,那爲了可以作到更高效,數據更小,咱們是否能夠把這個頭也去掉呢?
固然是能夠的,因而咱們的結構就變爲了只有對應的字段值了,這麼作的一個前提是:!!咱們必須清楚知道咱們識別出來二進制數據,其對應的具體是哪一個數據結構。!!
如今咱們去掉告終構描述,那怎麼可以作到更小呢?譬如一樣是int64,1 和 1<<32不必都用8字節來表示,譬如咱們能夠先對數據類型作一個編碼,而後緊跟着後續使用的bit,再跟着真正的數據。
每一個部分分別用幾個bit來表示呢?
那一個解決方法就是:咱們去掉有效字節的字段,咱們把這個是否有更多數據信息編碼進數據自己中,示意圖以下:
解決了編碼int類型的字段後,若是遇到string類型呢?這種類型首先也是數據類型描述,接着應該要是編碼後續有效字節,這是一個int,這能夠採用上面的方法來編碼,再跟着就是有效數據了。
目前protobuf支持的數據類型
上面有個主意的對於有符號數,咱們要單獨處理下,由於有符號數的最高位是經過0,1來表示正負的,可是上面編碼中最高位卻用來表示是否有後續數據了,因此咱們要經過ZigZag 編碼將有符號轉換爲無符號。
原理很簡單,就是經過下面的編碼方式: nginx
解決了編碼後,咱們來看最後一個問題:兼容性。
若是咱們改變了數據結構:新增或者刪除了字段怎麼辦???
這個也好解決,那咱們就給全部字段加上編號,經過字段來表示這個數據是結構體中哪一個字段,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的。