簡介
提到grpc就不得不提pb協(xié)議,protocol buffer是一個語言無關,平臺無關,可擴展的結(jié)構化數(shù)據(jù)序列化方案, 用于協(xié)議通訊, 數(shù)據(jù)存儲和其他更多用途。
gRPC的一個重要基石就是 Protocol Buffer 3, 這個版本(被稱為proto3)是原有Protocol Buffer 2(被稱為proto2)的升級版本,刪除了一部分特性,優(yōu)化了對移動設備的支持,另外增加了對android和ios的支持,使得gRPC可以順利的在移動設備上使用。
本內(nèi)容主要來自官方文檔 Protocol Buffers | Google Developers
后續(xù)用pb表示protocol buffers
本文主要使用C++和go進行介紹
概述
本文描述了如何使用pb語言來構建協(xié)議緩沖數(shù)據(jù)(即protocol + buffers),包括文件語法以及如何從文件生成對應代碼的數(shù)據(jù)訪問類。主要是針對Proto3版本的協(xié)議,參閱Proto3語言指南。
這是一個參考指南 - 更新的文檔請參閱教程。
關于3版本的協(xié)議和2版本的不再贅述,我們直接開始。
protocol buffers是什么?
Protocol buffer 是一個靈活,高效,自動化的結(jié)構化數(shù)據(jù)序列化機制 - 比如xml, 但是更小, 更快并且更簡單。一旦定義好數(shù)據(jù)如何構造, 就可以使用特殊的生成的源代碼來輕易的讀寫你的結(jié)構化數(shù)據(jù)到和從不同的數(shù)據(jù)流,用不同的語言。你甚至可以更新你的數(shù)據(jù)結(jié)構而不打破已部署的使用"舊有"格式編譯的程序。
How do they work?
通過在.proto文件中定義protocol buffer消息類型來指定要序列化的信息如何組織。每個protocol buffer消息是一個小的消息邏輯記錄,包含一序列的"名字-值"對。下面是一個非?;镜睦?,.proto文件定義了一個消息,包含一個人的消息:
message Person {
required string name = 1;
required int32 id = 2;
optional string email = 3;
enum PhoneType {
MOBILE = 0;
HOME = 1;
WORK = 2;
}
message PhoneNumber {
required string number = 1;
optional PhoneType type = 2 [default = HOME];
}
repeated PhoneNumber phone = 4;
}
如上述,消息格式很簡單 - 每個消息類型有一個或者多個唯一的編號的字段,而每個字段有一個名字和值類型,這里的值類型可以是數(shù)字(整型或者浮點),布爾,字符串,原始字節(jié)(raw bytes),或者甚至(如上面的例子)是其他protocol buffer消息,容許分層次的構建數(shù)據(jù)??梢灾付蛇x字段,必填字段和重復字段。
一旦定義了消息,可以在.proto文件上運行對應應用語言的protcol buffer的編譯器來生成數(shù)據(jù)訪問類。這些類為每個字段(類似name()或者set_name())提供簡單的訪問器,還有用于序列化/解析整個結(jié)構到/從原始字節(jié)的方法 - 因此,例如, 如果你選擇的語言是c++,在上面的例子上運行編譯器將會生成名為Person的類。然后可以用這個類在應用中獲取,序列化,并獲取 Person protocol buffer消息??赡茈S后編寫一些類似這樣的代碼:
Person person;
person.set_name("John Doe");
person.set_id(1234);
person.set_email("jdoe@example.com");
fstream output("myfile", ios::out | ios::binary);
person.SerializeToOstream(&output);
然后,稍后,可以這樣讀回消息:
fstream input("myfile", ios::in | ios::binary);
Person person;
person.ParseFromIstream(&input);
cout << "Name: " << person.name() << endl;
cout << "E-mail: " << person.email() << endl;
可以添加新的字段到消息格式中,而不破壞向后兼容;老的二進制在解析時簡單的忽略新的字段。因此,如果有一個使用protocol buffer作為數(shù)據(jù)格式的通訊協(xié)議,可以擴展協(xié)議而不必擔心打破已有代碼。
為什么不使用xml?
相比xml, Protocol buffer在序列化結(jié)構化數(shù)據(jù)方面有很多優(yōu)勢:
- 更簡單
- 小3 到 10 倍
- 快 20 到 100 倍
- 更清晰
- 生成數(shù)據(jù)訪問類, 更容易編程使用
例如,假設想要用 name 和 email 來構建一個 Person。在XML中,需要這樣做:
<person>
<name>John Doe</name>
<email>jdoe@example.com</email>
</person>
而對應的protocol buffer消息(使用protocol buffer 文本格式):
# protocol buffer的文本展示
# 這 *不是* 實際使用的二進制格式。
person {
name: "John Doe"
email: "jdoe@example.com"
}
當這個消息被編碼為protocol buffer 二進制格式(上面的文本格式僅僅是在調(diào)試和編輯時方便人閱讀的表示方式),它將可能是長28個字節(jié)并花費100-200納秒來解析。XML版本至少需要69個字節(jié),如果刪除空白字符,并將話費5000 - 10000 納秒來解析。
另外,操作protocol buffer也更簡單:
cout << "Name: " << person.name() << endl;
cout << "E-mail: " << person.email() << endl;
而使用XML,將不得不做類似的事情:
cout << "Name: "
<< person.getElementsByTagName("name")->item(0)->innerText()
<< endl;
cout << "E-mail: "
<< person.getElementsByTagName("email")->item(0)->innerText()
<< endl;
當然,Protocol buffer 也不總是比XML更合適 - 例如,Protocol buffer 不適合建?;谖谋镜臉酥?如HTML)文檔,因為無法輕易的使用文本交替結(jié)構。此外,XML是human-readable 和 human-editable的。Protocol buffer,至少他們原生的格式不是。XML也是某種程度上的自描述。Protocol buffer只有當有消息定義(.proto文件)時才有意義。
聽起來不錯!如何開始?
下載包 - 包含Java,Python和C++protocol buffer 編譯器的完整代碼,還有需要用于I/O和測試的類。要構建和安裝編譯器,請遵循README中的建議。