精品欧美一区二区三区在线观看 _久久久久国色av免费观看性色_国产精品久久在线观看_亚洲第一综合网站_91精品又粗又猛又爽_小泽玛利亚一区二区免费_91亚洲精品国偷拍自产在线观看 _久久精品视频在线播放_美女精品久久久_欧美日韩国产成人在线

如何使用Protobuf做數據交換

開發 后端
在以不同語言編寫并在不同平臺上運行的應用程序之間交換數據時,Protobuf 編碼可提高效率。

[[283226]]

在以不同語言編寫并在不同平臺上運行的應用程序之間交換數據時,Protobuf 編碼可提高效率。

協議緩沖區Protocol BuffersProtobufs)像 XML 和 JSON 一樣,可以讓用不同語言編寫并在不同平臺上運行的應用程序交換數據。例如,用 Go 編寫的發送程序可以在 Protobuf 中對以 Go 表示的銷售訂單數據進行編碼,然后用 Java 編寫的接收方可以對它進行解碼,以獲取所接收訂單數據的 Java 表示方式。這是在網絡連接上的結構示意圖:

Go 銷售訂單 —> Pbuf 編碼 —> 網絡 —> Pbuf 界面 —> Java 銷售訂單

與 XML 和 JSON 相比,Protobuf 編碼是二進制而不是文本,這會使調試復雜化。但是,正如本文中的代碼示例所確認的那樣,Protobuf 編碼在大小上比 XML 或 JSON 編碼要有效得多。

Protobuf 以另一種方式提供了這種有效性。在實現級別,Protobuf 和其他編碼系統對結構化數據進行序列化serialize反序列化deserialize。序列化將特定語言的數據結構轉換為字節流,反序列化是將字節流轉換回特定語言的數據結構的逆運算。序列化和反序列化可能成為數據交換的瓶頸,因為這些操作會占用大量 CPU。高效的序列化和反序列化是 Protobuf 的另一個設計目標。

最近的編碼技術,例如 Protobuf 和 FlatBuffers,源自 1990 年代初期的 DCE/RPC分布式計算環境/遠程過程調用Distributed Computing Environment/Remote Procedure Call)計劃。與 DCE/RPC 一樣,Protobuf 在數據交換中為 IDL(接口定義語言)和編碼層做出了貢獻。

本文將著眼于這兩層,然后提供 Go 和 Java 中的代碼示例以充實 Protobuf 的細節,并表明 Protobuf 是易于使用的。

Protobuf 作為一個 IDL 和編碼層

像 Protobuf 一樣,DCE/RPC 被設計為與語言和平臺無關。適當的庫和實用程序允許任何語言和平臺用于 DCE/RPC 領域。此外,DCE/RPC 體系結構非常優雅。IDL 文檔是一側的遠程過程與另一側的調用者之間的協定。Protobuf 也是以 IDL 文檔為中心的。

IDL 文檔是文本,在 DCE/RPC 中,使用基本 C 語法以及元數據的語法擴展(方括號)和一些新的關鍵字,例如 interface。這是一個例子:

  1. [uuid (2d6ead46-05e3-11ca-7dd1-426909beabcd), version(1.0)]
  2. interface echo {
  3.    const long int ECHO_SIZE = 512;
  4.    void echo(
  5.       [in]          handle_t h,
  6.       [in, string]  idl_char from_client[ ],
  7.       [out, string] idl_char from_service[ECHO_SIZE]
  8.    );
  9. }

該 IDL 文檔聲明了一個名為 echo 的過程,該過程帶有三個參數:類型為 handle_t(實現指針)和 idl_char(ASCII 字符數組)的 [in] 參數被傳遞給遠程過程,而 [out] 參數(也是一個字符串)從該過程中傳回。在此示例中,echo 過程不會顯式返回值(echo 左側的 void),但也可以返回值。返回值,以及一個或多個 [out] 參數,允許遠程過程任意返回許多值。下一節將介紹 Protobuf IDL,它的語法不同,但同樣用作數據交換中的協定。

DCE/RPC 和 Protobuf 中的 IDL 文檔是創建用于交換數據的基礎結構代碼的實用程序的輸入:

IDL 文檔 —> DCE/PRC 或 Protobuf 實用程序 —> 數據交換的支持代碼

作為相對簡單的文本,IDL 是同樣便于人類閱讀的關于數據交換細節的文檔(特別是交換的數據項的數量和每個項的數據類型)。

Protobuf 可用于現代 RPC 系統,例如 gRPC;但是 Protobuf 本身僅提供 IDL 層和編碼層,用于從發送者傳遞到接收者的消息。與原本的 DCE/RPC 一樣,Protobuf 編碼是二進制的,但效率更高。

目前,XML 和 JSON 編碼仍在通過 Web 服務等技術進行的數據交換中占主導地位,這些技術利用 Web 服務器、傳輸協議(例如 TCP、HTTP)以及標準庫和實用程序等原有的基礎設施來處理 XML 和 JSON 文檔。 此外,各種類型的數據庫系統可以存儲 XML 和 JSON 文檔,甚至舊式關系型系統也可以輕松生成查詢結果的 XML 編碼。現在,每種通用編程語言都具有支持 XML 和 JSON 的庫。那么,是什么讓我們回到 Protobuf 之類的二進制編碼系統呢?

讓我們看一下負十進制值 -128。以 2 的補碼二進制表示形式(在系統和語言中占主導地位)中,此值可以存儲在單個 8 位字節中:10000000。此整數值在 XML 或 JSON 中的文本編碼需要多個字節。例如,UTF-8 編碼需要四個字節的字符串,即 -128,即每個字符一個字節(十六進制,值為 0x2d0x310x320x38)。XML 和 JSON 還添加了標記字符,例如尖括號和大括號。有關 Protobuf 編碼的詳細信息下面就會介紹,但現在的關注點是一個通用點:文本編碼的壓縮性明顯低于二進制編碼。

在 Go 中使用 Protobuf 的示例

我的代碼示例著重于 Protobuf 而不是 RPC。以下是第一個示例的概述:

  • 名為 dataitem.proto 的 IDL 文件定義了一個 Protobuf 消息,它具有六個不同類型的字段:具有不同范圍的整數值、固定大小的浮點值以及兩個不同長度的字符串。
  • Protobuf 編譯器使用 IDL 文件生成 Go 版本(以及后面的 Java 版本)的 Protobuf 消息及支持函數。
  • Go 應用程序使用隨機生成的值填充原生的 Go 數據結構,然后將結果序列化為本地文件。為了進行比較, XML 和 JSON 編碼也被序列化為本地文件。
  • 作為測試,Go 應用程序通過反序列化 Protobuf 文件的內容來重建其原生數據結構的實例。
  • 作為語言中立性測試,Java 應用程序還會對 Protobuf 文件的內容進行反序列化以獲取原生數據結構的實例。

我的網站上提供了該 IDL 文件以及兩個 Go 和一個 Java 源文件,打包為 ZIP 文件。

最重要的 Protobuf IDL 文檔如下所示。該文檔存儲在文件 dataitem.proto 中,并具有常規的.proto 擴展名。

示例 1、Protobuf IDL 文檔

  1. syntax = "proto3";
  2.  
  3. package main;
  4.  
  5. message DataItem {
  6.   int64  oddA  = 1;
  7.   int64  evenA = 2;
  8.   int32  oddB  = 3;
  9.   int32  evenB = 4;
  10.   float  small = 5;
  11.   float  big   = 6;
  12.   string short = 7;
  13.   string long  = 8;
  14. }

該 IDL 使用當前的 proto3 而不是較早的 proto2 語法。軟件包名稱(在本例中為 main)是可選的,但是慣例使用它以避免名稱沖突。這個結構化的消息包含八個字段,每個字段都有一個 Protobuf 數據類型(例如,int64string)、名稱(例如,oddAshort)和一個等號 = 之后的數字標簽(即鍵)。標簽(在此示例中為 1 到 8)是唯一的整數標識符,用于確定字段序列化的順序。

Protobuf 消息可以嵌套到任意級別,而一個消息可以是另外一個消息的字段類型。這是一個使用 DataItem 消息作為字段類型的示例:

  1. message DataItems {
  2.   repeated DataItem item = 1;
  3. }

單個 DataItems 消息由重復的(零個或多個)DataItem 消息組成。

為了清晰起見,Protobuf 還支持枚舉類型:

  1. enum PartnershipStatus {
  2.   reserved "FREE", "CONSTRAINED", "OTHER";
  3. }

reserved 限定符確保用于實現這三個符號名的數值不能重復使用。

為了生成一個或多個聲明 Protobuf 消息結構的特定于語言的版本,包含這些結構的 IDL 文件被傳遞到protoc 編譯器(可在 Protobuf GitHub 存儲庫中找到)。對于 Go 代碼,可以以通常的方式安裝支持的 Protobuf 庫(這里以 作為命令行提示符):

  1. % go get github.com/golang/protobuf/proto

將 Protobuf IDL 文件 dataitem.proto 編譯為 Go 源代碼的命令是:

  1. % protoc --go_out=. dataitem.proto

標志 --go_out 指示編譯器生成 Go 源代碼。其他語言也有類似的標志。在這種情況下,結果是一個名為 dataitem.pb.go 的文件,該文件足夠小,可以將其基本內容復制到 Go 應用程序中。以下是生成的代碼的主要部分:

  1. var _ = proto.Marshal
  2.  
  3. type DataItem struct {
  4. OddA int64 `protobuf:"varint,1,opt,name=oddA" json:"oddA,omitempty"`
  5. EvenA int64 `protobuf:"varint,2,opt,name=evenA" json:"evenA,omitempty"`
  6. OddB int32 `protobuf:"varint,3,opt,name=oddB" json:"oddB,omitempty"`
  7. EvenB int32 `protobuf:"varint,4,opt,name=evenB" json:"evenB,omitempty"`
  8. Small float32 `protobuf:"fixed32,5,opt,name=small" json:"small,omitempty"`
  9. Big float32 `protobuf:"fixed32,6,opt,name=big" json:"big,omitempty"`
  10. Short string `protobuf:"bytes,7,opt,name=short" json:"short,omitempty"`
  11. Long string `protobuf:"bytes,8,opt,name=long" json:"long,omitempty"`
  12. }
  13.  
  14. func (m *DataItem) Reset() { *m = DataItem{} }
  15. func (m *DataItem) String() string { return proto.CompactTextString(m) }
  16. func (*DataItem) ProtoMessage() {}
  17. func init() {}

編譯器生成的代碼具有 Go 結構 DataItem,該結構導出 Go 字段(名稱現已大寫開頭),該字段與 Protobuf IDL 中聲明的名稱匹配。該結構字段具有標準的 Go 數據類型:int32int64float32string。在每個字段行的末尾,是描述 Protobuf 類型的字符串,提供 Protobuf IDL 文檔中的數字標簽及有關 JSON 信息的元數據,這將在后面討論。

此外也有函數;最重要的是 Proto.Marshal,用于將 DataItem 結構的實例序列化為 Protobuf 格式。輔助函數包括:清除 DataItem 結構的 Reset,生成 DataItem 的單行字符串表示的 String

描述 Protobuf 編碼的元數據應在更詳細地分析 Go 程序之前進行仔細研究。

Protobuf 編碼

Protobuf 消息的結構為鍵/值對的集合,其中數字標簽為鍵,相應的字段為值。字段名稱(例如,oddAsmall)是供人類閱讀的,但是 protoc 編譯器的確使用了字段名稱來生成特定于語言的對應名稱。例如,Protobuf IDL 中的 oddAsmall 名稱在 Go 結構中分別成為字段 OddASmall

鍵和它們的值都被編碼,但是有一個重要的區別:一些數字值具有固定大小的 32 或 64 位的編碼,而其他數字(包括消息標簽)則是 varint 編碼的,位數取決于整數的絕對值。例如,整數值 1 到 15 需要 8 位 varint 編碼,而值 16 到 2047 需要 16 位。varint 編碼在本質上與 UTF-8 編碼類似(但細節不同),它偏愛較小的整數值而不是較大的整數值。(有關詳細分析,請參見 Protobuf 編碼指南)結果是,Protobuf 消息應該在字段中具有較小的整數值(如果可能),并且鍵數應盡可能少,但每個字段至少得有一個鍵。

下表 1 列出了 Protobuf 編碼的要點:

編碼 示例類型 長度
varint int32uint32int64 可變長度
fixed fixed32floatdouble 固定的 32 位或 64 位長度
字節序列 stringbytes 序列長度

表 1. Protobuf 數據類型

未明確固定長度的整數類型是 varint 編碼的;因此,在 varint 類型中,例如 uint32u 代表無符號),數字 32 描述了整數的范圍(在這種情況下為 0 到 232 - 1),而不是其位的大小,該位大小取決于值。相比之下,對于固定長度類型(例如 fixed32double),Protobuf 編碼分別需要 32 位和 64 位。Protobuf 中的字符串是字節序列;因此,字段編碼的大小就是字節序列的長度。

另一個高效的方法值得一提。回想一下前面的示例,其中的 DataItems 消息由重復的 DataItem 實例組成:

  1. message DataItems {
  2.   repeated DataItem item = 1;
  3. }

repeated 表示 DataItem 實例是打包的:集合具有單個標簽,在這里是 1。因此,具有重復的 DataItem 實例的 DataItems 消息比具有多個但單獨的 DataItem 字段、每個字段都需要自己的標簽的消息的效率更高。

了解了這一背景,讓我們回到 Go 程序。

dataItem 程序的細節

dataItem 程序創建一個 DataItem 實例,并使用適當類型的隨機生成的值填充字段。Go 有一個 rand 包,帶有用于生成偽隨機整數和浮點值的函數,而我的 randString 函數可以從字符集中生成指定長度的偽隨機字符串。設計目標是要有一個具有不同類型和位大小的字段值的 DataItem 實例。例如,OddAEvenA 值分別是 64 位非負整數值的奇數和偶數;但是 OddBEvenB 變體的大小為 32 位,并存放 0 到 2047 之間的小整數值。隨機浮點值的大小為 32 位,字符串為 16(Short)和 32(Long)字符的長度。這是用隨機值填充 DataItem 結構的代碼段:

  1. // 可變長度整數
  2. n1 := rand.Int63() // 大整數
  3. if (n1 & 1) == 0 { n1++ } // 確保其是奇數
  4. ...
  5. n3 := rand.Int31() % UpperBound // 小整數
  6. if (n3 & 1) == 0 { n3++ } // 確保其是奇數
  7.  
  8. // 固定長度浮點數
  9. ...
  10. t1 := rand.Float32()
  11. t2 := rand.Float32()
  12. ...
  13. // 字符串
  14. str1 := randString(StrShort)
  15. str2 := randString(StrLong)
  16.  
  17. // 消息
  18. dataItem := &DataItem {
  19. OddA: n1,
  20. EvenA: n2,
  21. OddB: n3,
  22. EvenB: n4,
  23. Big: f1,
  24. Small: f2,
  25. Short: str1,
  26. Long: str2,
  27. }

創建并填充值后,DataItem 實例將以 XML、JSON 和 Protobuf 進行編碼,每種編碼均寫入本地文件:

  1. func encodeAndserialize(dataItem *DataItem) {
  2. bytes, _ := xml.MarshalIndent(dataItem, "", " ") // Xml to dataitem.xml
  3. ioutil.WriteFile(XmlFile, bytes, 0644) // 0644 is file access permissions
  4.  
  5. bytes, _ = json.MarshalIndent(dataItem, "", " ") // Json to dataitem.json
  6. ioutil.WriteFile(JsonFile, bytes, 0644)
  7.  
  8. bytes, _ = proto.Marshal(dataItem) // Protobuf to dataitem.pbuf
  9. ioutil.WriteFile(PbufFile, bytes, 0644)
  10. }

這三個序列化函數使用術語 marshal,它與 serialize 意思大致相同。如代碼所示,三個 Marshal 函數均返回一個字節數組,然后將其寫入文件。(為簡單起見,忽略可能的錯誤處理。)在示例運行中,文件大小為:

  1. dataitem.xml:  262 bytes
  2. dataitem.json: 212 bytes
  3. dataitem.pbuf:  88 bytes

Protobuf 編碼明顯小于其他兩個編碼方案。通過消除縮進字符(在這種情況下為空白和換行符),可以稍微減小 XML 和 JSON 序列化的大小。

以下是 dataitem.json 文件,該文件最終是由 json.MarshalIndent 調用產生的,并添加了以 ## 開頭的注釋:

  1. {
  2. "oddA": 4744002665212642479, ## 64-bit >= 0
  3. "evenA": 2395006495604861128, ## ditto
  4. "oddB": 57, ## 32-bit >= 0 but < 2048
  5. "evenB": 468, ## ditto
  6. "small": 0.7562016, ## 32-bit floating-point
  7. "big": 0.85202795, ## ditto
  8. "short": "ClH1oDaTtoX$HBN5", ## 16 random chars
  9. "long": "xId0rD3Cri%3Wt%^QjcFLJgyXBu9^DZI" ## 32 random chars
  10. }

盡管這些序列化的數據寫入到本地文件中,但是也可以使用相同的方法將數據寫入網絡連接的輸出流。

測試序列化和反序列化

Go 程序接下來通過將先前寫入 dataitem.pbuf 文件的字節反序列化為 DataItem 實例來運行基本測試。這是代碼段,其中去除了錯誤檢查部分:

  1. filebytes, err := ioutil.ReadFile(PbufFile) // get the bytes from the file
  2. ...
  3. testItem.Reset() // clear the DataItem structure
  4. err = proto.Unmarshal(filebytes, testItem) // deserialize into a DataItem instance

用于 Protbuf 反序列化的 proto.Unmarshal 函數與 proto.Marshal 函數相反。原始的 DataItem 和反序列化的副本將被打印出來以確認完全匹配:

  1. Original:
  2. 2041519981506242154 3041486079683013705 1192 1879
  3. 0.572123 0.326855
  4. boPb#T0O8Xd&Ps5EnSZqDg4Qztvo7IIs 9vH66AiGSQgCDxk&
  5.  
  6. Deserialized:
  7. 2041519981506242154 3041486079683013705 1192 1879
  8. 0.572123 0.326855
  9. boPb#T0O8Xd&Ps5EnSZqDg4Qztvo7IIs 9vH66AiGSQgCDxk&

一個 Java Protobuf 客戶端

用 Java 寫的示例是為了確認 Protobuf 的語言中立性。原始 IDL 文件可用于生成 Java 支持代碼,其中涉及嵌套類。但是,為了抑制警告信息,可以進行一些補充。這是修訂版,它指定了一個 DataMsg 作為外部類的名稱,內部類在該 Protobuf 消息后面自動命名為 DataItem

  1. syntax = "proto3";
  2.  
  3. package main;
  4.  
  5. option java_outer_classname = "DataMsg";
  6.  
  7. message DataItem {
  8. ...

進行此更改后,protoc 編譯與以前相同,只是所期望的輸出現在是 Java 而不是 Go:

  1. % protoc --java_out=. dataitem.proto

生成的源文件(在名為 main 的子目錄中)為 DataMsg.java,長度約為 1,120 行:Java 并不簡潔。編譯然后運行 Java 代碼需要具有 Protobuf 庫支持的 JAR 文件。該文件位于 Maven 存儲庫中。

放置好這些片段后,我的測試代碼相對較短(并且在 ZIP 文件中以 Main.java 形式提供):

  1. package main;
  2. import java.io.FileInputStream;
  3.  
  4. public class Main {
  5. public static void main(String[] args) {
  6. String path = "dataitem.pbuf"; // from the Go program's serialization
  7. try {
  8. DataMsg.DataItem deserial =
  9. DataMsg.DataItem.newBuilder().mergeFrom(new FileInputStream(path)).build();
  10.  
  11. System.out.println(deserial.getOddA()); // 64-bit odd
  12. System.out.println(deserial.getLong()); // 32-character string
  13. }
  14. catch(Exception e) { System.err.println(e); }
  15. }
  16. }

當然,生產級的測試將更加徹底,但是即使是該初步測試也可以證明 Protobuf 的語言中立性:dataitem.pbuf 文件是 Go 程序對 Go 語言版的 DataItem 進行序列化的結果,并且該文件中的字節被反序列化以產生一個 Java 語言的 DataItem 實例。Java 測試的輸出與 Go 測試的輸出相同。

用 numPairs 程序來結束

讓我們以一個示例作為結尾,來突出 Protobuf 效率,但又強調在任何編碼技術中都會涉及到的成本。考慮以下 Protobuf IDL 文件:

  1. syntax = "proto3";
  2. package main;
  3.  
  4. message NumPairs {
  5. repeated NumPair pair = 1;
  6. }
  7.  
  8. message NumPair {
  9. int32 odd = 1;
  10. int32 even = 2;
  11. }

NumPair 消息由兩個 int32 值以及每個字段的整數標簽組成。NumPairs 消息是嵌入的 NumPair 消息的序列。

Go 語言的 numPairs 程序(如下)創建了 200 萬個 NumPair 實例,每個實例都附加到 NumPairs 消息中。該消息可以按常規方式進行序列化和反序列化。

示例 2、numPairs 程序

  1. package main
  2.  
  3. import (
  4. "math/rand"
  5. "time"
  6. "encoding/xml"
  7. "encoding/json"
  8. "io/ioutil"
  9. "github.com/golang/protobuf/proto"
  10. )
  11.  
  12. // protoc-generated code: start
  13. var _ = proto.Marshal
  14. type NumPairs struct {
  15. Pair []*NumPair `protobuf:"bytes,1,rep,name=pair" json:"pair,omitempty"`
  16. }
  17.  
  18. func (m *NumPairs) Reset() { *m = NumPairs{} }
  19. func (m *NumPairs) String() string { return proto.CompactTextString(m) }
  20. func (*NumPairs) ProtoMessage() {}
  21. func (m *NumPairs) GetPair() []*NumPair {
  22. if m != nil { return m.Pair }
  23. return nil
  24. }
  25.  
  26. type NumPair struct {
  27. Odd int32 `protobuf:"varint,1,opt,name=odd" json:"odd,omitempty"`
  28. Even int32 `protobuf:"varint,2,opt,name=even" json:"even,omitempty"`
  29. }
  30.  
  31. func (m *NumPair) Reset() { *m = NumPair{} }
  32. func (m *NumPair) String() string { return proto.CompactTextString(m) }
  33. func (*NumPair) ProtoMessage() {}
  34. func init() {}
  35. // protoc-generated code: finish
  36.  
  37. var numPairsStruct NumPairs
  38. var numPairs = &numPairsStruct
  39.  
  40. func encodeAndserialize() {
  41. // XML encoding
  42. filename := "./pairs.xml"
  43. bytes, _ := xml.MarshalIndent(numPairs, "", " ")
  44. ioutil.WriteFile(filename, bytes, 0644)
  45.  
  46. // JSON encoding
  47. filename = "./pairs.json"
  48. bytes, _ = json.MarshalIndent(numPairs, "", " ")
  49. ioutil.WriteFile(filename, bytes, 0644)
  50.  
  51. // ProtoBuf encoding
  52. filename = "./pairs.pbuf"
  53. bytes, _ = proto.Marshal(numPairs)
  54. ioutil.WriteFile(filename, bytes, 0644)
  55. }
  56.  
  57. const HowMany = 200 * 100 * 100 // two million
  58.  
  59. func main() {
  60. rand.Seed(time.Now().UnixNano())
  61.  
  62. // uncomment the modulus operations to get the more efficient version
  63. for i := 0; i < HowMany; i++ {
  64. n1 := rand.Int31() // % 2047
  65. if (n1 & 1) == 0 { n1++ } // ensure it's odd
  66. n2 := rand.Int31() // % 2047
  67. if (n2 & 1) == 1 { n2++ } // ensure it's even
  68.  
  69. next := &NumPair {
  70. Odd: n1,
  71. Even: n2,
  72. }
  73. numPairs.Pair = append(numPairs.Pair, next)
  74. }
  75. encodeAndserialize()
  76. }

每個 NumPair 中隨機生成的奇數和偶數值的范圍在 0 到 20 億之間變化。就原始數據(而非編碼數據)而言,Go 程序中生成的整數總共為 16MB:每個 NumPair 為兩個整數,總計為 400 萬個整數,每個值的大小為四個字節。

為了進行比較,下表列出了 XML、JSON 和 Protobuf 編碼的示例 NumsPairs 消息的 200 萬個 NumPair 實例。原始數據也包括在內。由于 numPairs 程序生成隨機值,因此樣本運行的輸出有所不同,但接近表中顯示的大小。

編碼 文件 字節大小 Pbuf/其它 比例
pairs.raw 16MB 169%
Protobuf pairs.pbuf 27MB
JSON pairs.json 100MB 27%
XML pairs.xml 126MB 21%

表 2. 16MB 整數的編碼開銷

不出所料,Protobuf 和之后的 XML 和 JSON 差別明顯。Protobuf 編碼大約是 JSON 的四分之一,是 XML 的五分之一。但是原始數據清楚地表明 Protobuf 也會產生編碼開銷:序列化的 Protobuf 消息比原始數據大 11MB。包括 Protobuf 在內的任何編碼都涉及結構化數據,這不可避免地會增加字節。

序列化的 200 萬個 NumPair 實例中的每個實例都包含個整數值:Go 結構中的 EvenOdd 字段分別一個,而 Protobuf 編碼中的每個字段、每個標簽一個。對于原始數據(而不是編碼數據),每個實例將達到 16 個字節,樣本 NumPairs 消息中有 200 萬個實例。但是 Protobuf 標記(如 NumPair 字段中的 int32 值)使用 varint 編碼,因此字節長度有所不同。特別是,小的整數值(在這種情況下,包括標簽在內)需要不到四個字節進行編碼。

如果對 numPairs 程序進行了修改,以使兩個 NumPair 字段的值小于 2048,且其編碼為一或兩個字節,則 Protobuf 編碼將從 27MB 下降到 16MB,這正是原始數據的大小。下表總結了樣本運行中的新編碼大小。

編碼 文件 字節大小 Pbuf/其它 比例
None pairs.raw 16MB 100%
Protobuf pairs.pbuf 16MB
JSON pairs.json 77MB 21%
XML pairs.xml 103MB 15%

表 3. 編碼 16MB 的小于 2048 的整數

總之,修改后的 numPairs 程序的字段值小于 2048,可減少原始數據中每個四字節整數值的大小。但是 Protobuf 編碼仍然需要標簽,這些標簽會在 Protobuf 消息中添加字節。Protobuf 編碼確實會增加消息大小,但是如果要編碼相對較小的整數值(無論是字段還是鍵),則可以通過 varint 因子來減少此開銷。

對于包含混合類型的結構化數據(且整數值相對較小)的中等大小的消息,Protobuf 明顯優于 XML 和 JSON 等選項。在其他情況下,數據可能不適合 Protobuf 編碼。例如,如果兩個應用程序需要共享大量文本記錄或大整數值,則可以采用壓縮而不是編碼技術。 

責任編輯:龐桂玉 來源: Linux中國
相關推薦

2010-01-15 10:19:42

數據交換技術

2018-08-31 21:00:39

數據交換模型數據模型應用程序

2009-01-03 14:54:40

ibmdwXML

2010-03-02 10:50:57

WCF元數據交換

2011-08-19 13:45:14

iPhone應用iPhone OS數據

2009-06-22 17:57:26

IExtendProv

2010-02-04 11:15:12

數據交換技術

2012-01-04 00:10:52

ibmdw

2009-01-19 09:28:42

JSONJavaScriptJSON結構

2023-04-10 16:25:37

區塊鏈去中心化安全

2010-01-06 14:36:04

JSON插件

2009-11-06 10:25:34

WCF元數據交換

2010-02-04 11:20:29

網絡數據交換技術

2009-11-06 10:45:47

WCF服務元數據交換

2010-02-04 11:32:01

數據交換技術

2010-01-08 13:40:26

2010-01-20 14:34:48

數據交換技術

2009-11-09 17:17:31

WCF元數據交換

2012-09-26 09:51:11

電子政務數據交換

2009-11-06 10:37:57

配置WCF服務
點贊
收藏

51CTO技術棧公眾號

成人国产一区二区三区精品麻豆| 日本久久一级片| 午夜片欧美伦| 日韩欧美中文字幕公布| 日本手机在线视频| 免费毛片在线| 美女高潮久久久| 欧美成人激情视频| 91黄色免费视频| 日韩av中字| 亚洲人吸女人奶水| 久久综合中文色婷婷| 91tv国产成人福利| 亚洲久久一区| 精品国产自在精品国产浪潮| 男人的天堂影院| 国产激情久久| 精品国产91久久久久久| 亚洲人体一区| 日韩中文字幕综合| 免费欧美日韩国产三级电影| 久久国产色av| 国产毛片久久久久久久| 亚洲3区在线| 色婷婷久久久综合中文字幕| 成人黄色片免费| 番号集在线观看| 99在线热播精品免费| 91精品久久久久久久久久久久久 | 一级片视频免费| 99精品国产一区二区青青牛奶 | 色呦呦呦在线观看| 日本一区二区三区高清不卡| 精品免费日产一区一区三区免费| 国产精品福利电影| 日韩成人午夜电影| 欧美最猛性xxxxx亚洲精品| 九九热国产在线| 999精品视频| 伊人精品在线观看| 色婷婷av777| 啪啪激情综合网| 日韩精品一区二区三区中文精品| 免费成年人高清视频| 精品无人乱码一区二区三区 | 色青青草原桃花久久综合| jizz日本免费| 亚洲ab电影| 亚洲国产天堂久久国产91 | 国产欧美视频在线| 欧美日韩成人综合| 日本xxxx黄色| 秋霞国产精品| 欧美午夜寂寞影院| 538在线视频观看| 偷拍中文亚洲欧美动漫| 一本色道综合亚洲| 无码精品a∨在线观看中文| 菠萝蜜视频在线观看www入口| 亚洲私人影院在线观看| 椎名由奈jux491在线播放| 欧美一区二区三区| 国产精品久久久久久一区二区三区 | 99久久综合国产精品| 成人精品水蜜桃| 亚洲国产精品suv| 不卡的av电影在线观看| 久久99精品国产一区二区三区| 日本韩国免费观看| 国产精品一二三四五区| 亚洲综合日本| 欧洲亚洲妇女av| 色一情一乱一伦| 久久久久国产一区二区| 日韩免费观看av| 久久这里只有精品9| 日本视频一区二区三区| 国产精品久久久久久久久久新婚| 18国产免费视频| 久久99热国产| 成人h视频在线观看| 香蕉视频网站在线| 欧美国产精品专区| 亚洲五码在线观看视频| missav|免费高清av在线看| 亚洲一区二区欧美日韩| 久久国产成人精品国产成人亚洲 | 日韩av电影手机在线观看| 成人免费视频国产免费| 久久成人久久鬼色| 懂色一区二区三区av片| 美女欧美视频在线观看免费 | 户外露出一区二区三区| 欧美日韩一级视频| 性猛交╳xxx乱大交| 亚洲图片久久| 久久久精品久久| 日韩av在线播| 青青草一区二区三区| 91深夜福利视频| 天堂中文字幕av| 国产精品午夜电影| 国产激情片在线观看| 精品国产免费人成网站| 91麻豆精品91久久久久久清纯 | 欧美中文字幕在线观看视频| 亚洲优女在线| 91.com在线观看| 在线观看国产三级| 欧美电影《睫毛膏》| 午夜精品久久久久久久久久久久久| 久久精品99北条麻妃| 国产69精品久久99不卡| 亚洲精品高清视频| 久久青草伊人| 日韩午夜小视频| 黄色国产在线播放| 亚洲综合社区| 成人精品一二区| 国产h在线观看| 午夜私人影院久久久久| 亚洲免费成人在线视频| 亚洲资源网站| 97久久精品人人澡人人爽缅北| 亚洲影院一区二区三区| 久久人人97超碰com| 日本福利视频在线观看| 亚洲视频自拍| 夜夜躁日日躁狠狠久久88av| 五月天激情四射| 成人午夜激情片| 色乱码一区二区三区熟女| 欧美精品高清| 亚洲美女动态图120秒| 美国黄色小视频| 久久99深爱久久99精品| 色狠狠久久av五月综合|| 亚洲私拍视频| 亚洲精品国产精品久久清纯直播| 久久亚洲av午夜福利精品一区| 久久成人羞羞网站| 色大师av一区二区三区| 一区二区三区四区日本视频| 亚洲精品美女视频| 91看片在线播放| 不卡区在线中文字幕| 福利视频免费在线观看| 亚洲一区 二区| 色综合老司机第九色激情| 国产视频在线观看视频| 国产精品短视频| 国产成年人视频网站| 日韩欧美午夜| 成人h猎奇视频网站| 91caoporn在线| 欧美日韩成人在线一区| 亚洲精品卡一卡二| 国产一区二区三区香蕉| 好色先生视频污| 日韩高清在线观看一区二区| 九九热这里只有精品免费看| 亚洲黄色在线播放| 亚洲成人资源在线| 免费a级黄色片| 久久精品导航| 亚洲免费视频一区| 粉嫩av国产一区二区三区| 久久国产精品久久国产精品| 精品国产乱码久久久久久蜜臀网站| 亚洲视频网在线直播| 成人做爰69片免费| 99精品热6080yy久久| 蜜桃网站成人| 亚洲男人在线| 欧美精品在线观看| 全国男人的天堂网| 一本一本大道香蕉久在线精品| 五月婷婷欧美激情| 国产一区二区影院| av一区二区三区免费观看| 日韩精品导航| 国产精品人成电影| 手机av免费在线| 亚洲精品国产免费| 中文字幕+乱码+中文乱码91| 成人免费在线播放视频| wwwxx日本| 久久九九电影| 好色先生视频污| 亚洲成人一品| 91精品啪aⅴ在线观看国产| 国产又色又爽又黄刺激在线视频| 亚洲男人天天操| 国产精品欧美综合亚洲| 亚洲国产精品久久久男人的天堂 | 久久久久久网址| 久久久久久久久亚洲精品| 欧美嫩在线观看| 国产成人无码精品| 国产精品毛片高清在线完整版| 一区二区在线免费观看视频| 久久亚洲欧洲| 大陆极品少妇内射aaaaaa| 国产亚洲一区二区三区啪| 99re视频| av成人在线播放| 隔壁老王国产在线精品| 男人影院在线观看| 精品一区二区亚洲| 成 人 免费 黄 色| 欧美色国产精品| 精品国产免费观看| **欧美大码日韩| 黄免费在线观看| 成人福利视频在线| 亚洲制服中文字幕| 日本伊人色综合网| 欧美深夜福利视频| 欧美午夜在线| 一区二区不卡在线观看| 亚洲激情播播| 高清日韩一区| 成人噜噜噜噜| 国产精品视频自在线| 成人小电影网站| 久久久久久亚洲| 影音先锋在线播放| 久久久精品欧美| av在线免费观看网站| 亚洲精品一区久久久久久| 精品毛片一区二区三区| 欧美人与禽zozo性伦| 中文字幕手机在线视频| 欧美日韩亚洲一区二区| 久热这里只有精品在线| 亚洲男人的天堂在线aⅴ视频 | 亚洲一二三区不卡| 欧美成人综合色| 亚洲免费视频成人| 蜜桃av免费观看| 欧美国产一区二区| 中字幕一区二区三区乱码| 久久久国产综合精品女国产盗摄| 欧美成人三级伦在线观看| 成人av网站免费| 99热超碰在线| 成人国产精品免费观看视频| 好吊操视频这里只有精品| 国产精品亚洲专一区二区三区| 在线播放av中文字幕| 激情五月婷婷综合| 国产欧美一区二| 激情综合网最新| 亚洲无在线观看| 国产又粗又猛又爽又黄91精品| 91看片破解版| 国产麻豆精品theporn| 国产精品探花在线播放| 国产一区二区三区综合| 极品人妻一区二区| 成人免费高清在线| 亚洲av无码国产精品久久| 91论坛在线播放| 中文字幕网站在线观看| 国产精品久久久久影院亚瑟| 日本一级片免费| 一二三四社区欧美黄| 日本熟妇乱子伦xxxx| 色综合天天综合色综合av| 亚洲午夜无码久久久久| 欧美日韩国产美| 性一交一乱一透一a级| 亚洲成人动漫在线播放| 牛牛热在线视频| 久久韩国免费视频| 国产福利在线免费观看| 欧美影院久久久| 成人自拍视频网| 99精彩视频在线观看免费| 午夜精品影视国产一区在线麻豆| 日韩欧美在线观看强乱免费| 国产精品videosex性欧美| 日本男女交配视频| 免费永久网站黄欧美| jizz18女人| 成人在线一区二区三区| 国产精品毛片一区二区| 亚洲精品视频在线看| 国产精品自拍99| 欧美日韩国产一级二级| 老司机午夜福利视频| 国产一级揄自揄精品视频| av官网在线播放| 欧美一区在线直播| 豆花视频一区| 欧美精品欧美精品| 亚洲精品888| 免费大片在线观看| 国产一区二区三区不卡在线观看 | 中文字幕巨乱亚洲| 久久香蕉精品视频| 欧美视频在线观看一区二区| 国精品人妻无码一区二区三区喝尿| 亚洲欧美日韩中文在线| 污视频网站免费在线观看| 国产精品第2页| 国语一区二区三区| 亚洲蜜桃av| 美女被久久久| 丰满岳乱妇一区二区| 日韩一区在线播放| 亚洲天堂男人av| 精品乱码亚洲一区二区不卡| 91在线播放网站| 国产91精品久久久久久久| 午夜视频在线观看精品中文| 亚洲一卡二卡区| 新67194成人永久网站| 中文字幕第六页| 国产精品久久久久久一区二区三区| 美日韩一二三区 | 免费在线国产| 欧美—级高清免费播放| 亚洲黑人在线| 日韩欧美视频一区二区| 99国产精品久久久久久久成人热 | 日本精品一区二区三区在线观看视频| 日本免费高清一区二区| 国产亚洲午夜| 亚洲av成人片无码| 亚洲综合一二区| 国内精品国产成人国产三级| 丝袜情趣国产精品| 欧洲av一区二区| 欧美精品一区二区视频| 一本色道久久精品| 美女伦理水蜜桃4| 亚洲欧美偷拍三级| 国产免费无遮挡| 中文字幕在线看视频国产欧美在线看完整 | 欧美在线观看www| www.成人网.com| 国产亚洲成人av| 欧美大片在线观看| 亚洲91av| 国产不卡一区二区在线观看| 牛牛国产精品| 中文字幕人妻无码系列第三区| 日韩一区欧美小说| 国产乱码精品一区二区三区精东| 中文字幕av一区二区三区谷原希美| 成人做爰视频www网站小优视频| 麻豆亚洲一区| 蜜桃av综合| 久久久精品成人| 精品视频色一区| 麻豆视频在线播放| 成人免费看黄网站| 综合一区二区三区| 特黄特黄一级片| 亚洲一区二区三区四区不卡| 少妇精品视频一区二区| 啪一啪鲁一鲁2019在线视频| 国产精品一区二区三区av麻| 浓精h攵女乱爱av| 中文字幕一区二区三区在线观看 | 激情图区综合网| 丝袜 亚洲 另类 欧美 重口| 日韩欧美高清dvd碟片| heyzo高清在线| 久热国产精品视频一区二区三区 | 免费国偷自产拍精品视频| 亚洲综合免费观看高清完整版在线 | 在线观看美女av| 精品日产卡一卡二卡麻豆| 免费看男女www网站入口在线 | 激情小视频在线观看| 国产精品久久久久久久app| 99久久婷婷| 在线播放第一页| 欧美天堂在线观看| 9色在线视频| 国产精品一区二区三区在线 | 亚洲综合一二三区| 久久经典视频| 亚洲www视频| 一区二区三区导航| 国产又粗又猛又爽又黄的视频小说 | 大肉大捧一进一出好爽动态图| 国产精品私人自拍| 亚洲av无码乱码国产精品| 欧美亚洲视频在线观看| 亚洲成人tv| 一级特黄a大片免费| 欧美日精品一区视频| 欧美人与禽猛交乱配| 天堂av一区二区| 风间由美一区二区三区在线观看| 亚洲av无码精品一区二区 | 中文av在线全新|