如何使用 Protobuf 做資料交換
在以不同語言編寫並在不同平臺上執行的應用程式之間交換資料時,Protobuf 編碼可提高效率。
協議緩衝區(Protobufs)像 XML 和 JSON 一樣,可以讓用不同語言編寫並在不同平臺上執行的應用程式交換資料。例如,用 Go 編寫的傳送程式可以在 Protobuf 中對以 Go 表示的銷售訂單資料進行編碼,然後用 Java 編寫的接收方可以對它進行解碼,以獲取所接收訂單資料的 Java 表示方式。這是在網路連線上的結構示意圖:
Go 銷售訂單 —> Pbuf 編碼 —> 網路 —> Pbuf 介面 —> Java 銷售訂單
與 XML 和 JSON 相比,Protobuf 編碼是二進位制而不是文字,這會使除錯複雜化。但是,正如本文中的程式碼示例所確認的那樣,Protobuf 編碼在大小上比 XML 或 JSON 編碼要有效得多。
Protobuf 以另一種方式提供了這種有效性。在實現級別,Protobuf 和其他編碼系統對結構化資料進行序列化和反序列化。序列化將特定語言的資料結構轉換為位元組流,反序列化是將位元組流轉換回特定語言的資料結構的逆運算。序列化和反序列化可能成為資料交換的瓶頸,因為這些操作會佔用大量 CPU。高效的序列化和反序列化是 Protobuf 的另一個設計目標。
最近的編碼技術,例如 Protobuf 和 FlatBuffers,源自 1990 年代初期的 DCE/RPC(分散式計算環境/遠端過程呼叫)計劃。與 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
。這是一個例子:
[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 文件宣告瞭一個名為 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
,即每個字元一個位元組(十六進位制,值為 0x2d
、0x31
、0x32
和 0x38
)。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 文件
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 使用當前的 proto3 而不是較早的 proto2 語法。軟體包名稱(在本例中為 main
)是可選的,但是慣例使用它以避免名稱衝突。這個結構化的訊息包含八個欄位,每個欄位都有一個 Protobuf 資料型別(例如,int64
、string
)、名稱(例如,oddA
、short
)和一個等號 =
之後的數字標籤(即鍵)。標籤(在此示例中為 1 到 8)是唯一的整數識別符號,用於確定欄位序列化的順序。
Protobuf 訊息可以巢狀到任意級別,而一個訊息可以是另外一個訊息的欄位型別。這是一個使用 DataItem
訊息作為欄位型別的示例:
message DataItems {
repeated DataItem item = 1;
}
單個 DataItems
訊息由重複的(零個或多個)DataItem
訊息組成。
為了清晰起見,Protobuf 還支援列舉型別:
enum PartnershipStatus {
reserved "FREE", "CONSTRAINED", "OTHER";
}
reserved
限定符確保用於實現這三個符號名的數值不能重複使用。
為了生成一個或多個宣告 Protobuf 訊息結構的特定於語言的版本,包含這些結構的 IDL 檔案被傳遞到protoc
編譯器(可在 Protobuf GitHub 儲存庫中找到)。對於 Go 程式碼,可以以通常的方式安裝支援的 Protobuf 庫(這裡以 %
作為命令列提示符):
% go get github.com/golang/protobuf/proto
將 Protobuf IDL 檔案 dataitem.proto
編譯為 Go 原始碼的命令是:
% protoc --go_out=. dataitem.proto
標誌 --go_out
指示編譯器生成 Go 原始碼。其他語言也有類似的標誌。在這種情況下,結果是一個名為 dataitem.pb.go
的檔案,該檔案足夠小,可以將其基本內容複製到 Go 應用程式中。以下是生成的程式碼的主要部分:
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 結構 DataItem
,該結構匯出 Go 欄位(名稱現已大寫開頭),該欄位與 Protobuf IDL 中宣告的名稱匹配。該結構欄位具有標準的 Go 資料型別:int32
、int64
、float32
和 string
。在每個欄位行的末尾,是描述 Protobuf 型別的字串,提供 Protobuf IDL 文件中的數字標籤及有關 JSON 資訊的後設資料,這將在後面討論。
此外也有函式;最重要的是 Proto.Marshal
,用於將 DataItem
結構的例項序列化為 Protobuf 格式。輔助函式包括:清除 DataItem
結構的 Reset
,生成 DataItem
的單行字串表示的 String
。
描述 Protobuf 編碼的後設資料應在更詳細地分析 Go 程式之前進行仔細研究。
Protobuf 編碼
Protobuf 訊息的結構為鍵/值對的集合,其中數字標籤為鍵,相應的欄位為值。欄位名稱(例如,oddA
和 small
)是供人類閱讀的,但是 protoc
編譯器的確使用了欄位名稱來生成特定於語言的對應名稱。例如,Protobuf IDL 中的 oddA
和 small
名稱在 Go 結構中分別成為欄位 OddA
和 Small
。
鍵和它們的值都被編碼,但是有一個重要的區別:一些數字值具有固定大小的 32 或 64 位的編碼,而其他數字(包括訊息標籤)則是 varint
編碼的,位數取決於整數的絕對值。例如,整數值 1 到 15 需要 8 位 varint
編碼,而值 16 到 2047 需要 16 位。varint
編碼在本質上與 UTF-8 編碼類似(但細節不同),它偏愛較小的整數值而不是較大的整數值。(有關詳細分析,請參見 Protobuf 編碼指南)結果是,Protobuf 訊息應該在欄位中具有較小的整數值(如果可能),並且鍵數應儘可能少,但每個欄位至少得有一個鍵。
下表 1 列出了 Protobuf 編碼的要點:
編碼 | 示例型別 | 長度 |
---|---|---|
varint |
int32 、uint32 、int64 |
可變長度 |
fixed |
fixed32 、float 、double |
固定的 32 位或 64 位長度 |
位元組序列 | string 、bytes |
序列長度 |
表 1. Protobuf 資料型別
未明確固定長度的整數型別是 varint
編碼的;因此,在 varint
型別中,例如 uint32
(u
代表無符號),數字 32 描述了整數的範圍(在這種情況下為 0 到 232 - 1),而不是其位的大小,該位大小取決於值。相比之下,對於固定長度型別(例如 fixed32
或 double
),Protobuf 編碼分別需要 32 位和 64 位。Protobuf 中的字串是位元組序列;因此,欄位編碼的大小就是位元組序列的長度。
另一個高效的方法值得一提。回想一下前面的示例,其中的 DataItems
訊息由重複的 DataItem
例項組成:
message DataItems {
repeated DataItem item = 1;
}
repeated
表示 DataItem
例項是打包的:集合具有單個標籤,在這裡是 1。因此,具有重複的 DataItem
例項的 DataItems
訊息比具有多個但單獨的 DataItem
欄位、每個欄位都需要自己的標籤的訊息的效率更高。
瞭解了這一背景,讓我們回到 Go 程式。
dataItem 程式的細節
dataItem
程式建立一個 DataItem
例項,並使用適當型別的隨機生成的值填充欄位。Go 有一個 rand
包,帶有用於生成偽隨機整數和浮點值的函式,而我的 randString
函式可以從字符集中生成指定長度的偽隨機字串。設計目標是要有一個具有不同型別和位大小的欄位值的 DataItem
例項。例如,OddA
和 EvenA
值分別是 64 位非負整數值的奇數和偶數;但是 OddB
和 EvenB
變體的大小為 32 位,並存放 0 到 2047 之間的小整數值。隨機浮點值的大小為 32 位,字串為 16(Short
)和 32(Long
)字元的長度。這是用隨機值填充 DataItem
結構的程式碼段:
// 可變長度整數
n1 := rand.Int63() // 大整數
if (n1 & 1) == 0 { n1++ } // 確保其是奇數
...
n3 := rand.Int31() % UpperBound // 小整數
if (n3 & 1) == 0 { n3++ } // 確保其是奇數
// 固定長度浮點數
...
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,
}
建立並填充值後,DataItem
例項將以 XML、JSON 和 Protobuf 進行編碼,每種編碼均寫入本地檔案:
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)
}
這三個序列化函式使用術語 marshal
,它與 serialize
意思大致相同。如程式碼所示,三個 Marshal
函式均返回一個位元組陣列,然後將其寫入檔案。(為簡單起見,忽略可能的錯誤處理。)在示例執行中,檔案大小為:
dataitem.xml: 262 bytes
dataitem.json: 212 bytes
dataitem.pbuf: 88 bytes
Protobuf 編碼明顯小於其他兩個編碼方案。通過消除縮排字元(在這種情況下為空白和換行符),可以稍微減小 XML 和 JSON 序列化的大小。
以下是 dataitem.json
檔案,該檔案最終是由 json.MarshalIndent
呼叫產生的,並新增了以 ##
開頭的註釋:
{
"oddA": 4744002665212642479, ## 64-bit >= 0
"evenA": 2395006495604861128, ## ditto
"oddB": 57, ## 32-bit >= 0 but < 2048
"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
}
儘管這些序列化的資料寫入到本地檔案中,但是也可以使用相同的方法將資料寫入網路連線的輸出流。
測試序列化和反序列化
Go 程式接下來通過將先前寫入 dataitem.pbuf
檔案的位元組反序列化為 DataItem
例項來執行基本測試。這是程式碼段,其中去除了錯誤檢查部分:
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
函式與 proto.Marshal
函式相反。原始的 DataItem
和反序列化的副本將被列印出來以確認完全匹配:
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&
一個 Java Protobuf 客戶端
用 Java 寫的示例是為了確認 Protobuf 的語言中立性。原始 IDL 檔案可用於生成 Java 支援程式碼,其中涉及巢狀類。但是,為了抑制警告資訊,可以進行一些補充。這是修訂版,它指定了一個 DataMsg
作為外部類的名稱,內部類在該 Protobuf 訊息後面自動命名為 DataItem
:
syntax = "proto3";
package main;
option java_outer_classname = "DataMsg";
message DataItem {
...
進行此更改後,protoc
編譯與以前相同,只是所期望的輸出現在是 Java 而不是 Go:
% protoc --java_out=. dataitem.proto
生成的原始檔(在名為 main
的子目錄中)為 DataMsg.java
,長度約為 1,120 行:Java 並不簡潔。編譯然後執行 Java 程式碼需要具有 Protobuf 庫支援的 JAR 檔案。該檔案位於 Maven 儲存庫中。
放置好這些片段後,我的測試程式碼相對較短(並且在 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); }
}
}
當然,生產級的測試將更加徹底,但是即使是該初步測試也可以證明 Protobuf 的語言中立性:dataitem.pbuf
檔案是 Go 程式對 Go 語言版的 DataItem
進行序列化的結果,並且該檔案中的位元組被反序列化以產生一個 Java 語言的 DataItem
例項。Java 測試的輸出與 Go 測試的輸出相同。
用 numPairs 程式來結束
讓我們以一個示例作為結尾,來突出 Protobuf 效率,但又強調在任何編碼技術中都會涉及到的成本。考慮以下 Protobuf IDL 檔案:
syntax = "proto3";
package main;
message NumPairs {
repeated NumPair pair = 1;
}
message NumPair {
int32 odd = 1;
int32 even = 2;
}
NumPair
訊息由兩個 int32
值以及每個欄位的整數標籤組成。NumPairs
訊息是嵌入的 NumPair
訊息的序列。
Go 語言的 numPairs
程式(如下)建立了 200 萬個 NumPair
例項,每個例項都附加到 NumPairs
訊息中。該訊息可以按常規方式進行序列化和反序列化。
示例 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 < HowMany; i++ {
n1 := rand.Int31() // % 2047
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()
}
每個 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 結構中的 Even
和 Odd
欄位分別一個,而 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 編碼。例如,如果兩個應用程式需要共享大量文字記錄或大整數值,則可以採用壓縮而不是編碼技術。
via: https://opensource.com/article/19/10/protobuf-data-interchange
作者:Marty Kalin 選題:lujun9972 譯者:wxy 校對:wxy
訂閱“Linux 中國”官方小程式來檢視
相關文章
- 資料交換利器 Protobuf 技術淺析
- 我們如何使用CRM做資料分析?
- protobuf資料型別資料型別
- 交換資料
- flask非同步資料交換celery的使用Flask非同步
- 使用Bundle在Activity之間交換資料
- 交換機是如何轉發資料包的?
- 如何在前端中使用protobuf(node篇)前端
- 如何在前端中使用protobuf(vue篇)前端Vue
- 如何用第三層交換保證資料安全
- 大資料:protobuf是啥玩意兒大資料
- 在.NET使用JSON作為資料交換格式JSON
- 如何在C#中使用Google.Protobuf工具C#Go
- 如何用python做資料分析Python
- 大資料時代,如何做資料探勘與分析!大資料
- php 中使用 protobufPHP
- java中使用protobufJava
- 資料交換格式:Protocol BufferProtocol
- python-資料交換Python
- 前後端資料互動利器--Protobuf後端
- 詳解通訊資料協議ProtoBuf協議
- Netty整合SpringBoot並使用Protobuf進行資料傳輸NettySpring Boot
- 如何用CRM系統做資料分析
- 如何做資料視覺化分析視覺化
- 如何做資料庫最佳化資料庫
- vue的axios元件如何與PHP後端交換資料VueiOS元件PHP後端
- 如何使用Finder在Mac上檢視資料夾大小?這樣做Mac
- Python使用Protobuf&&如何賦值&&如何正反序列化Python賦值
- 在 Golang 中使用 ProtobufGolang
- Protobuf Java使用嚮導Java
- Google Protobuf 使用 Java 版GoJava
- Burpsuite中protobuf資料流的解析UI
- Protobuf: 高效資料傳輸的秘密武器
- 高效的資料壓縮編碼方式 Protobuf
- MysqlsqoopHDFS 資料交換實驗MySqlOOP
- 如何透過一條資料庫語句做資料分析資料庫
- rabbitmq 如何在 docker 下做資料持久化?MQDocker持久化
- 大量資料如何做分頁處理