日韩无码专区无码一级三级片|91人人爱网站中日韩无码电影|厨房大战丰满熟妇|AV高清无码在线免费观看|另类AV日韩少妇熟女|中文日本大黄一级黄色片|色情在线视频免费|亚洲成人特黄a片|黄片wwwav色图欧美|欧亚乱色一区二区三区

RELATEED CONSULTING
相關(guān)咨詢
選擇下列產(chǎn)品馬上在線溝通
服務(wù)時(shí)間:8:30-17:00
你可能遇到了下面的問題
關(guān)閉右側(cè)工具欄

新聞中心

這里有您想知道的互聯(lián)網(wǎng)營銷解決方案
通過Protobuf進(jìn)行數(shù)據(jù)交換

protobuf是由Google開發(fā)的一套對(duì)數(shù)據(jù)結(jié)構(gòu)進(jìn)行序列化的方法,可用做通信協(xié)議,數(shù)據(jù)存儲(chǔ)格式,等等。其特點(diǎn)是不限語言、不限平臺(tái)、擴(kuò)展性強(qiáng),就像XML一樣。

Protobuf 作為一個(gè) IDL 和編碼層

像 Protobuf 一樣,DCE/RPC 被設(shè)計(jì)為與語言和平臺(tái)無關(guān)。適當(dāng)?shù)膸旌蛯?shí)用程序允許任何語言和平臺(tái)用于 DCE/RPC 領(lǐng)域。此外,DCE/RPC 體系結(jié)構(gòu)非常優(yōu)雅。IDL 文檔是一側(cè)的遠(yuǎn)程過程與另一側(cè)的調(diào)用者之間的協(xié)定。Protobuf 也是以 IDL 文檔為中心的。

IDL 文檔是文本,在 DCE/RPC 中,使用基本 C 語法以及元數(shù)據(jù)的語法擴(kuò)展(方括號(hào))和一些新的關(guān)鍵字,例如 interface。這是一個(gè)例子:

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

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

DCE/RPC 和 Protobuf 中的 IDL 文檔是創(chuàng)建用于交換數(shù)據(jù)的基礎(chǔ)結(jié)構(gòu)代碼的實(shí)用程序的輸入: IDL 文檔 —> DCE/PRC 或 Protobuf 實(shí)用程序 —> 數(shù)據(jù)交換的支持代碼 作為相對(duì)簡單的文本,IDL 是同樣便于人類閱讀的關(guān)于數(shù)據(jù)交換細(xì)節(jié)的文檔(特別是交換的數(shù)據(jù)項(xiàng)的數(shù)量和每個(gè)項(xiàng)的數(shù)據(jù)類型)。

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

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

讓我們看一下負(fù)十進(jìn)制值 -128。以 2 的補(bǔ)碼二進(jìn)制表示形式(在系統(tǒng)和語言中占主導(dǎo)地位)中,此值可以存儲(chǔ)在單個(gè) 8 位字節(jié)中:10000000。此整數(shù)值在 XML 或 JSON 中的文本編碼需要多個(gè)字節(jié)。例如,UTF-8 編碼需要四個(gè)字節(jié)的字符串,即 -128,即每個(gè)字符一個(gè)字節(jié)(十六進(jìn)制,值為 0x2d、0x31、0x32 和 0x38)。XML 和 JSON 還添加了標(biāo)記字符,例如尖括號(hào)和大括號(hào)。有關(guān) Protobuf 編碼的詳細(xì)信息下面就會(huì)介紹,但現(xiàn)在的關(guān)注點(diǎn)是一個(gè)通用點(diǎn):文本編碼的壓縮性明顯低于二進(jìn)制編碼。

在 Go 中使用 Protobuf 的示例

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

名為 dataitem.proto 的 IDL 文件定義了一個(gè) Protobuf 消息,它具有六個(gè)不同類型的字段:具有不同范圍的整數(shù)值、固定大小的浮點(diǎn)值以及兩個(gè)不同長度的字符串。 Protobuf 編譯器使用 IDL 文件生成 Go 版本(以及后面的 Java 版本)的 Protobuf 消息及支持函數(shù)。 Go 應(yīng)用程序使用隨機(jī)生成的值填充原生的 Go 數(shù)據(jù)結(jié)構(gòu),然后將結(jié)果序列化為本地文件。為了進(jìn)行比較, XML 和 JSON 編碼也被序列化為本地文件。 作為測試,Go 應(yīng)用程序通過反序列化 Protobuf 文件的內(nèi)容來重建其原生數(shù)據(jù)結(jié)構(gòu)的實(shí)例。 作為語言中立性測試,Java 應(yīng)用程序還會(huì)對(duì) Protobuf 文件的內(nèi)容進(jìn)行反序列化以獲取原生數(shù)據(jù)結(jié)構(gòu)的實(shí)例。 我的網(wǎng)站上提供了該 IDL 文件以及兩個(gè) Go 和一個(gè) Java 源文件,打包為 ZIP 文件。

最重要的 Protobuf IDL 文檔如下所示。該文檔存儲(chǔ)在文件 dataitem.proto 中,并具有常規(guī)的.proto 擴(kuò)展名。

示例 1、Protobuf IDL 文檔

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

該 IDL 使用當(dāng)前的 proto3 而不是較早的 proto2 語法。軟件包名稱(在本例中為 main)是可選的,但是慣例使用它以避免名稱沖突。這個(gè)結(jié)構(gòu)化的消息包含八個(gè)字段,每個(gè)字段都有一個(gè) Protobuf 數(shù)據(jù)類型(例如,int64、string)、名稱(例如,oddA、short)和一個(gè)等號(hào) = 之后的數(shù)字標(biāo)簽(即鍵)。標(biāo)簽(在此示例中為 1 到 8)是唯一的整數(shù)標(biāo)識(shí)符,用于確定字段序列化的順序。

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

message DataItems {
 repeated DataItem item = 1;
}

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

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

enum PartnershipStatus {
 reserved "FREE", "CONSTRAINED", "OTHER";
}

reserved 限定符確保用于實(shí)現(xiàn)這三個(gè)符號(hào)名的數(shù)值不能重復(fù)使用。

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

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

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

% protoc --go_out=. dataitem.proto

標(biāo)志 –go_out 指示編譯器生成 Go 源代碼。其他語言也有類似的標(biāo)志。在這種情況下,結(jié)果是一個(gè)名為 dataitem.pb.go 的文件,該文件足夠小,可以將其基本內(nèi)容復(fù)制到 Go 應(yīng)用程序中。以下是生成的代碼的主要部分:

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

編譯器生成的代碼具有 Go 結(jié)構(gòu) DataItem,該結(jié)構(gòu)導(dǎo)出 Go 字段(名稱現(xiàn)已大寫開頭),該字段與 Protobuf IDL 中聲明的名稱匹配。該結(jié)構(gòu)字段具有標(biāo)準(zhǔn)的 Go 數(shù)據(jù)類型:int32、int64、float32 和 string。在每個(gè)字段行的末尾,是描述 Protobuf 類型的字符串,提供 Protobuf IDL 文檔中的數(shù)字標(biāo)簽及有關(guān) JSON 信息的元數(shù)據(jù),這將在后面討論。

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

描述 Protobuf 編碼的元數(shù)據(jù)應(yīng)在更詳細(xì)地分析 Go 程序之前進(jìn)行仔細(xì)研究。

Protobuf 編碼

Protobuf 消息的結(jié)構(gòu)為鍵/值對(duì)的集合,其中數(shù)字標(biāo)簽為鍵,相應(yīng)的字段為值。字段名稱(例如,oddA 和 small)是供人類閱讀的,但是 protoc 編譯器的確使用了字段名稱來生成特定于語言的對(duì)應(yīng)名稱。例如,Protobuf IDL 中的 oddA 和 small 名稱在 Go 結(jié)構(gòu)中分別成為字段 OddA 和 Small。

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

下表 1 列出了 Protobuf 編碼的要點(diǎn):

編碼 示例類型 長度
varint int32uint32、int64 可變長度
fixed fixed32、float、double 固定的 32 位或 64 位長度
字節(jié)序列 string、bytes 序列長度

表 1. Protobuf 數(shù)據(jù)類型

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

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

message DataItems {
 repeated DataItem item = 1;
}

repeated 表示 DataItem 實(shí)例是打包的:集合具有單個(gè)標(biāo)簽,在這里是 1。因此,具有重復(fù)的 DataItem 實(shí)例的 DataItems 消息比具有多個(gè)但單獨(dú)的 DataItem 字段、每個(gè)字段都需要自己的標(biāo)簽的消息的效率更高。

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

dataItem 程序的細(xì)節(jié)

dataItem 程序創(chuàng)建一個(gè) DataItem 實(shí)例,并使用適當(dāng)類型的隨機(jī)生成的值填充字段。Go 有一個(gè) rand 包,帶有用于生成偽隨機(jī)整數(shù)和浮點(diǎn)值的函數(shù),而我的 randString 函數(shù)可以從字符集中生成指定長度的偽隨機(jī)字符串。設(shè)計(jì)目標(biāo)是要有一個(gè)具有不同類型和位大小的字段值的 DataItem 實(shí)例。例如,OddA 和 EvenA 值分別是 64 位非負(fù)整數(shù)值的奇數(shù)和偶數(shù);但是 OddB 和 EvenB 變體的大小為 32 位,并存放 0 到 2047 之間的小整數(shù)值。隨機(jī)浮點(diǎn)值的大小為 32 位,字符串為 16(Short)和 32(Long)字符的長度。這是用隨機(jī)值填充 DataItem 結(jié)構(gòu)的代碼段:

// 可變長度整數(shù)
n1 := rand.Int63()        // 大整數(shù)
if (n1 & 1) == 0 { n1++ } // 確保其是奇數(shù)
...
n3 := rand.Int31() % UpperBound // 小整數(shù)
if (n3 & 1) == 0 { n3++ }       // 確保其是奇數(shù)
// 固定長度浮點(diǎn)數(shù)
...
t1 := rand.Float32()
t2 := rand.Float32()
...
// 字符串
str1 := randString(StrShort)
str2 := randString(StrLong)
// 消息
dataItem := &DataItem {
  OddA:  n1,
  EvenA: n2,
  OddB:  n3,
  EvenB: n4,
  Big:   f1,
  Small: f2,
  Short: str1,
  Long:  str2,
}

創(chuàng)建并填充值后,DataItem 實(shí)例將以 XML、JSON 和 Protobuf 進(jìn)行編碼,每種編碼均寫入本地文件:

func encodeAndserialize(dataItem *DataItem) {
  bytes, _ := xml.MarshalIndent(dataItem, "", " ")  // Xml to dataitem.xml
  ioutil.WriteFile(XmlFile, bytes, 0644)            // 0644 is file access permissions
  bytes, _ = json.MarshalIndent(dataItem, "", " ")  // Json to dataitem.json
  ioutil.WriteFile(JsonFile, bytes, 0644)
  bytes, _ = proto.Marshal(dataItem)                // Protobuf to dataitem.pbuf
  ioutil.WriteFile(PbufFile, bytes, 0644)
}

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

dataitem.xml:  262 bytes
dataitem.json: 212 bytes
dataitem.pbuf:  88 bytes

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

以下是 dataitem.json 文件,該文件最終是由 json.MarshalIndent 調(diào)用產(chǎn)生的,并添加了以 ## 開頭的注釋:

{
"oddA":  4744002665212642479,                ## 64-bit >= 0
"evenA": 2395006495604861128,                ## ditto
"oddB":  57,                                 ## 32-bit >= 0 but  "evenB": 468,                                ## ditto "small": 0.7562016,                          ## 32-bit floating-point "big":   0.85202795,                         ## ditto "short": "ClH1oDaTtoX$HBN5",                 ## 16 random chars "long":  "xId0rD3Cri%3Wt%^QjcFLJgyXBu9^DZI"  ## 32 random chars } 

盡管這些序列化的數(shù)據(jù)寫入到本地文件中,但是也可以使用相同的方法將數(shù)據(jù)寫入網(wǎng)絡(luò)連接的輸出流。

測試序列化和反序列化

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

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

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

Original:
2041519981506242154 3041486079683013705 1192 1879
0.572123 0.326855
boPb#T0O8Xd&Ps5EnSZqDg4Qztvo7IIs 9vH66AiGSQgCDxk&
Deserialized:
2041519981506242154 3041486079683013705 1192 1879
0.572123 0.326855
boPb#T0O8Xd&Ps5EnSZqDg4Qztvo7IIs 9vH66AiGSQgCDxk&

一個(gè) Java Protobuf 客戶端

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

syntax = "proto3";
package main;
option java_outer_classname = "DataMsg";
message DataItem {
...

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

% protoc --java_out=. dataitem.proto

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

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

package main;
import java.io.FileInputStream;
public class Main {
  public static void main(String[] args) {
     String path = "dataitem.pbuf";  // from the Go program's serialization      try {         DataMsg.DataItem deserial =           DataMsg.DataItem.newBuilder().mergeFrom(new FileInputStream(path)).build();         System.out.println(deserial.getOddA()); // 64-bit odd         System.out.println(deserial.getLong()); // 32-character string      }      catch(Exception e) { System.err.println(e); }    } } 

當(dāng)然,生產(chǎn)級(jí)的測試將更加徹底,但是即使是該初步測試也可以證明 Protobuf 的語言中立性:dataitem.pbuf 文件是 Go 程序?qū)?Go 語言版的 DataItem 進(jìn)行序列化的結(jié)果,并且該文件中的字節(jié)被反序列化以產(chǎn)生一個(gè) Java 語言的 DataItem 實(shí)例。Java 測試的輸出與 Go 測試的輸出相同。

用 numPairs 程序來結(jié)束

讓我們以一個(gè)示例作為結(jié)尾,來突出 Protobuf 效率,但又強(qiáng)調(diào)在任何編碼技術(shù)中都會(huì)涉及到的成本??紤]以下 Protobuf IDL 文件:

syntax = "proto3";
package main;
message NumPairs {
 repeated NumPair pair = 1;
}
message NumPair {
 int32 odd = 1;
 int32 even = 2;
}

NumPair 消息由兩個(gè) int32 值以及每個(gè)字段的整數(shù)標(biāo)簽組成。NumPairs 消息是嵌入的 NumPair 消息的序列。

Go 語言的 numPairs 程序(如下)創(chuàng)建了 200 萬個(gè) NumPair 實(shí)例,每個(gè)實(shí)例都附加到 NumPairs 消息中。該消息可以按常規(guī)方式進(jìn)行序列化和反序列化。

示例 2、numPairs 程序

package main
import (
  "math/rand"
  "time"
  "encoding/xml"
  "encoding/json"
  "io/ioutil"
  "github.com/golang/protobuf/proto"
)
// protoc-generated code: start
var _ = proto.Marshal
type NumPairs struct {
  Pair []*NumPair `protobuf:"bytes,1,rep,name=pair" json:"pair,omitempty"`
}
func (m *NumPairs) Reset()         { *m = NumPairs{} }
func (m *NumPairs) String() string { return proto.CompactTextString(m) }
func (*NumPairs) ProtoMessage()    {}
func (m *NumPairs) GetPair() []*NumPair {
  if m != nil { return m.Pair }
  return nil
}
type NumPair struct {
  Odd  int32 `protobuf:"varint,1,opt,name=odd" json:"odd,omitempty"`
  Even int32 `protobuf:"varint,2,opt,name=even" json:"even,omitempty"`
}
func (m *NumPair) Reset()         { *m = NumPair{} }
func (m *NumPair) String() string { return proto.CompactTextString(m) }
func (*NumPair) ProtoMessage()    {}
func init() {}
// protoc-generated code: finish
var numPairsStruct NumPairs
var numPairs = &numPairsStruct
func encodeAndserialize() {
  // XML encoding
  filename := "./pairs.xml"
  bytes, _ := xml.MarshalIndent(numPairs, "", " ")
  ioutil.WriteFile(filename, bytes, 0644)
  // JSON encoding
  filename = "./pairs.json"
  bytes, _ = json.MarshalIndent(numPairs, "", " ")
  ioutil.WriteFile(filename, bytes, 0644)
  // ProtoBuf encoding
  filename = "./pairs.pbuf"
  bytes, _ = proto.Marshal(numPairs)
  ioutil.WriteFile(filename, bytes, 0644)
}
const HowMany = 200 * 100  * 100 // two million
func main() {
  rand.Seed(time.Now().UnixNano())
  // uncomment the modulus operations to get the more efficient version
  for i := 0; i if (n1 & 1) == 0 { n1++ } // ensure it's odd      n2 := rand.Int31() // % 2047      if (n2 & 1) == 1 { n2++ } // ensure it's even
     next := &NumPair {
                Odd:  n1,
                Even: n2,
             }
     numPairs.Pair = append(numPairs.Pair, next)
  }
  encodeAndserialize()

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

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

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

表 2. 16MB 整數(shù)的編碼開銷

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

序列化的 200 萬個(gè) NumPair 實(shí)例中的每個(gè)實(shí)例都包含四個(gè)整數(shù)值:Go 結(jié)構(gòu)中的 Even 和 Odd 字段分別一個(gè),而 Protobuf 編碼中的每個(gè)字段、每個(gè)標(biāo)簽一個(gè)。對(duì)于原始數(shù)據(jù)(而不是編碼數(shù)據(jù)),每個(gè)實(shí)例將達(dá)到 16 個(gè)字節(jié),樣本 NumPairs 消息中有 200 萬個(gè)實(shí)例。但是 Protobuf 標(biāo)記(如 NumPair 字段中的 int32 值)使用 varint 編碼,因此字節(jié)長度有所不同。特別是,小的整數(shù)值(在這種情況下,包括標(biāo)簽在內(nèi))需要不到四個(gè)字節(jié)進(jìn)行編碼。

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

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

表 3. 編碼 16MB 的小于 2048 的整數(shù)

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

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


網(wǎng)頁題目:通過Protobuf進(jìn)行數(shù)據(jù)交換
網(wǎng)站路徑:http://m.5511xx.com/article/djdchii.html