Protobuf3學(xué)習(xí)筆記

本文是對Protobuf3(以下簡稱pb)官方文檔的學(xué)習(xí)筆記浙芙,大部分示例摘自官方。

原文:https://developers.google.com/protocol-buffers/docs/proto3

一個簡單的例子

syntax = "proto3";

message SearchRequest {
  string query = 1;
  int32 page_number = 2;
  int32 result_per_page = 3;
}

版本號

對于一個pb文件而言卢厂,文件首個非空、非注釋的行必須注明pb的版本惠啄,即syntax = "proto3";慎恒,否則默認(rèn)版本是proto2。

Message

一個message類型看上去很像一個Java class撵渡,由多個字段組成融柬。每一個字段都由類型、名稱組成趋距,位于等號右邊的值不是字段默認(rèn)值粒氧,而是數(shù)字標(biāo)簽,可以理解為字段身份的標(biāo)識符节腐,類似于數(shù)據(jù)庫中的主鍵外盯,不可重復(fù),標(biāo)識符用于在編譯后的二進(jìn)制消息格式中對字段進(jìn)行識別翼雀,一旦你的pb消息投入使用饱苟,字段的標(biāo)識就不應(yīng)該再改變。數(shù)字標(biāo)簽的范圍是[1, 536870911]狼渊,其中19000~19999是保留數(shù)字箱熬。

類型

每個字段的類型(int32,string)都是scalar的類型,和其他語言類型的對比如下:

類型對比.png

修飾符

如果一個字段被repeated修飾,則表示它是一個列表類型的字段城须,如下所示:

...
message SearchRequest {
  repeated string args = 1 // 等價于java中的List<string> args
}

如果你希望可以預(yù)留一些數(shù)字標(biāo)簽或者字段可以使用reserved修飾符:

message Foo {
  reserved 2, 15, 9 to 11;
  reserved "foo", "bar";
  string foo = 3 // 編譯報錯蚤认,因為‘foo’已經(jīng)被標(biāo)為保留字段
}

默認(rèn)值

  1. string類型的默認(rèn)值是空字符串
  2. bytes類型的默認(rèn)值是空字節(jié)
  3. bool類型的默認(rèn)值是false
  4. 數(shù)字類型的默認(rèn)值是0
  5. enum類型的默認(rèn)值是第一個定義的枚舉值
  6. message類型(對象,如上文的SearchRequest就是message類型)的默認(rèn)值與 語言 相關(guān)
  7. repeated修飾的字段默認(rèn)值是空列表

如果一個字段的值等于默認(rèn)值(如bool類型的字段設(shè)為false)酿傍,那么它將不會被序列化烙懦,這樣的設(shè)計是為了節(jié)省流量。

枚舉

每個枚舉值有對應(yīng)的數(shù)值赤炒,數(shù)值不一定是連續(xù)的氯析。第一個枚舉值的數(shù)值必須是0且至少有一個枚舉值,否則編譯報錯莺褒。編譯后編譯器會為你生成對應(yīng)語言的枚舉類掩缓。

message SearchRequest {
  string query = 1;
  int32 page_number = 2;
  int32 result_per_page = 3;
  enum Corpus {
    UNIVERSAL = 0;
    WEB = 1;
    IMAGES = 2;
    LOCAL = 3;
    NEWS = 4;
    PRODUCTS = 5;
    VIDEO = 6;
  }
  Corpus corpus = 4;
}

一個數(shù)值可以對應(yīng)多個枚舉值,必須標(biāo)明option allow_alias = true;

enum EnumAllowingAlias {
  option allow_alias = true;
  UNKNOWN = 0;
  STARTED = 1;
  RUNNING = 1;
}

可以使用MessageType.EnumType的形式引用定義在其它message類型中的枚舉遵岩。

由于編碼原因你辣,出于效率考慮,官方不推薦使用負(fù)數(shù)作為枚舉值的數(shù)值尘执。

使用其它的message類型

除了上述基本類型舍哄,一個字段的類型也可以是其它的message類型:

message SearchResponse {
  repeated Result results = 1;
}

message Result {
  string url = 1;
  string title = 2;
  repeated string snippets = 3;
}

從上面的例子可以看到,一個.proto文件中可以定義多個message誊锭。我們也可以引用定義在其它文件中的message:

import "myproject/other_protos.proto"; // 這樣就可以引用在other_protos.proto文件中定義的message

不能導(dǎo)入不使用的.proto文件表悬。

import還有一種特殊的語法,先看下面的例子:

// new.proto
// 原來在old.proto文件中的定義移到這里
// old.proto
import public "new.proto"; // 把引用傳遞給上層使用方
import "other.proto"; // 引用old.proto本身使用的定義
// client.proto
import "old.proto";
// 此處可以引用old.proto和new.proto中的定義丧靡,但不能使用other.proto中的定義

從這個例子中可以看到import關(guān)鍵字導(dǎo)入的定義僅在當(dāng)前文件有效蟆沫,不能被上層使用方引用(client.proto無法使用other.proto中的定義),而import public關(guān)鍵字導(dǎo)入的定義可以被上層使用方引用(client.proto可以使用new.proto中的定義)温治,import public的功能可以看作是import的超集饭庞,在import的功能上還具有傳遞引用的作用。

嵌套類型

你可以在一個message類型中定義另一個message類型熬荆,并且可以一直嵌套下去舟山,類似Java的內(nèi)部類:

message SearchResponse {
  message Result {
    string url = 1;
    string title = 2;
    repeated string snippets = 3;
  }
  repeated Result results = 1;
}

可以使用Parent.Type的形式引用嵌套的message:

message SomeOtherMessage {
  SearchResponse.Result result = 1;
}

Any

Any類型允許包裝任意的message類型:

import "google/protobuf/any.proto";

message Response {
    google.protobuf.Any data = 1;
}

可以通過pack()unpack()(方法名在不同的語言中可能不同)方法裝箱/拆箱,以下是Java的例子:

People people = People.newBuilder().setName("proto").setAge(1).build();
// protoc編譯后生成的message類
Response r = Response.newBuilder().setData(Any.pack(people)).build();
// 使用Response包裝people

System.out.println(r.getData().getTypeUrl());
// type.googleapis.com/example.protobuf.people.People
System.out.println(r.getData().unpack(People.class).getName());
// proto

Any對包裝的類型會生成一個URL惶看,默認(rèn)是type.googleapis.com/packagename.messagename(在Java中可以通過這個特性進(jìn)行反射操作)捏顺。

Oneof

如果你有一些字段同時最多只有一個能被設(shè)置,可以使用oneof關(guān)鍵字來實現(xiàn)纬黎,任何一個字段被設(shè)置幅骄,其它字段會自動被清空(被設(shè)為默認(rèn)值):

message SampleMessage {
  oneof test_oneof {
    string name = 4;
    SubMessage sub_message = 9;
  }
}

oneof塊中的字段不支持repeated

Maps

pb中也可以使用map類型(官方并不認(rèn)為是一種類型本今,此處稱之為類型僅便于理解)拆座,絕大多數(shù)scalar類型都可以作為key主巍,除了浮點型和bytes,枚舉型也不能作為key挪凑,value可以是除了map以外的任意類型:

// map<key_type, value_type> map_field = N;
map<string, Project> projects = 3;

map類型字段不支持repeated孕索,value的順序是不定的。

map其實是一種語法糖躏碳,它等價于以下形式:

message MapFieldEntry {
  key_type key = 1;
  value_type value = 2;
}

repeated MapFieldEntry map_field = N;

你可以用指定package以避免類型命名沖突:

package foo.bar;
message Open { ... }

然后可以用類型的全限定名來引用它:

message Foo {
  ...
  foo.bar.Open open = 1;
  ...
}

指定包名后搞旭,會對生成的代碼產(chǎn)生影響,以Java為例菇绵,生成的類會以你指定的package作為包名肄渗。

JSON映射

pb支持和JSON互相轉(zhuǎn)換。如果一個字段不存在JSON數(shù)據(jù)中或者為null咬最,那么pb中會被賦為該字段的默認(rèn)值翎嫡,反之,如果一個字段在pb中是默認(rèn)值永乌,那么不會寫到JSON數(shù)據(jù)中以節(jié)省空間惑申。

PB<>JSON

選項

選項不對message的定義產(chǎn)生任何的效果,只會在一些特定的場景中起到作用翅雏,下面是一部分例子圈驼,完整的選項列表可以前往google/protobuf/descriptor.proto查看(Java語言可以在jar包中找到):

  1. option java_package = "com.example.foo"; 編譯器為以此作為生成的Java類的包名,如果沒有該選項望几,則會以pb的package作為包名碗脊。
  2. option java_multiple_files = true; 該選項為true時,生成的Java類將是包級別的橄妆,否則會在一個包裝類中。
  3. option optimize_for = CODE_SIZE; 該選項會對生成的類產(chǎn)生影響祈坠,作用是根據(jù)指定的選項對代碼進(jìn)行不同方面的優(yōu)化害碾。
  4. int32 old_field = 6 [deprecated=true]; 把字段標(biāo)為過時的。

Java例子

最后赦拘,用Java寫了一個簡單的例子:Github

謝謝慌随。

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市躺同,隨后出現(xiàn)的幾起案子阁猜,更是在濱河造成了極大的恐慌,老刑警劉巖蹋艺,帶你破解...
    沈念sama閱讀 211,639評論 6 492
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件剃袍,死亡現(xiàn)場離奇詭異,居然都是意外死亡捎谨,警方通過查閱死者的電腦和手機民效,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 90,277評論 3 385
  • 文/潘曉璐 我一進(jìn)店門憔维,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人畏邢,你說我怎么就攤上這事业扒。” “怎么了舒萎?”我有些...
    開封第一講書人閱讀 157,221評論 0 348
  • 文/不壞的土叔 我叫張陵程储,是天一觀的道長。 經(jīng)常有香客問我臂寝,道長章鲤,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 56,474評論 1 283
  • 正文 為了忘掉前任交煞,我火速辦了婚禮咏窿,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘素征。我一直安慰自己集嵌,他們只是感情好,可當(dāng)我...
    茶點故事閱讀 65,570評論 6 386
  • 文/花漫 我一把揭開白布御毅。 她就那樣靜靜地躺著根欧,像睡著了一般。 火紅的嫁衣襯著肌膚如雪端蛆。 梳的紋絲不亂的頭發(fā)上凤粗,一...
    開封第一講書人閱讀 49,816評論 1 290
  • 那天,我揣著相機與錄音今豆,去河邊找鬼嫌拣。 笑死,一個胖子當(dāng)著我的面吹牛呆躲,可吹牛的內(nèi)容都是我干的异逐。 我是一名探鬼主播,決...
    沈念sama閱讀 38,957評論 3 408
  • 文/蒼蘭香墨 我猛地睜開眼插掂,長吁一口氣:“原來是場噩夢啊……” “哼灰瞻!你這毒婦竟也來了?” 一聲冷哼從身側(cè)響起辅甥,我...
    開封第一講書人閱讀 37,718評論 0 266
  • 序言:老撾萬榮一對情侶失蹤酝润,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后璃弄,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體要销,經(jīng)...
    沈念sama閱讀 44,176評論 1 303
  • 正文 獨居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 36,511評論 2 327
  • 正文 我和宋清朗相戀三年夏块,在試婚紗的時候發(fā)現(xiàn)自己被綠了蕉陋。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片捐凭。...
    茶點故事閱讀 38,646評論 1 340
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖凳鬓,靈堂內(nèi)的尸體忽然破棺而出茁肠,到底是詐尸還是另有隱情,我是刑警寧澤缩举,帶...
    沈念sama閱讀 34,322評論 4 330
  • 正文 年R本政府宣布垦梆,位于F島的核電站,受9級特大地震影響仅孩,放射性物質(zhì)發(fā)生泄漏托猩。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點故事閱讀 39,934評論 3 313
  • 文/蒙蒙 一辽慕、第九天 我趴在偏房一處隱蔽的房頂上張望京腥。 院中可真熱鬧,春花似錦溅蛉、人聲如沸公浪。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,755評論 0 21
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽欠气。三九已至,卻和暖如春镜撩,著一層夾襖步出監(jiān)牢的瞬間预柒,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 31,987評論 1 266
  • 我被黑心中介騙來泰國打工袁梗, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留宜鸯,地道東北人。 一個月前我還...
    沈念sama閱讀 46,358評論 2 360
  • 正文 我出身青樓遮怜,卻偏偏與公主長得像顾翼,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子奈泪,可洞房花燭夜當(dāng)晚...
    茶點故事閱讀 43,514評論 2 348

推薦閱讀更多精彩內(nèi)容

  • 由于工程項目中擬采用一種簡便高效的數(shù)據(jù)交換格式,百度了一下發(fā)現(xiàn)除了采用 xml灸芳、JSON 還有 ProtoBuf(...
    黃海佳閱讀 48,580評論 1 23
  • Spring Cloud為開發(fā)人員提供了快速構(gòu)建分布式系統(tǒng)中一些常見模式的工具(例如配置管理涝桅,服務(wù)發(fā)現(xiàn),斷路器烙样,智...
    卡卡羅2017閱讀 134,633評論 18 139
  • 什么是protocal buffer? protocal buffer 以下簡稱protobuf是google 的...
    碎念楓子閱讀 3,811評論 8 10
  • 1840年冯遂,英國對中國發(fā)動了鴉片戰(zhàn)爭。號稱亞洲第一強國的中國被打得一敗涂地谒获,清朝出動了比英軍多得多的軍隊蛤肌,但最終還...
    悠悠千古事閱讀 821評論 0 1
  • 我和榕樹閱讀 115評論 0 0