Golang gRPC實踐 連載三 Protobuf語法

Protobuf語法

gRPC推薦使用proto3,本節只介紹經常使用語法,更多高級使用姿式請參考官方文檔java

Message定義

一個message類型定義描述了一個請求或相應的消息格式,能夠包含多種類型字段。例如定義一個搜索請求的消息格式,每一個請求包含查詢字符串、頁碼、每頁數目。python

syntax = "proto3";

message SearchRequest {
    string query = 1;           // 查詢字符串
    int32  page_number = 2;     // 頁碼
    int32  result_per_page = 3; // 每頁條數
}

首行聲明使用的protobuf版本爲proto3git

SearchRequest 定義了三個字段,每一個字段聲明以分號結尾,可以使用雙斜線//添加註釋。github

字段類型聲明

全部的字段須要前置聲明數據類型,上面的示例指定了兩個數值類型和一個字符串類型。除了基本的標量類型還有複合類型,如枚舉、其它message類型等。golang

標識符Tags

能夠看到,消息的定義中,每一個字段都有一個惟一的數值型標識符。這些標識符用於標識字段在消息中的二進制格式,使用中的類型不該該隨意改動。須要注意的是,[1-15]內的標識在編碼時只佔用一個字節,包含標識符和字段類型。[16-2047]之間的標識符佔用2個字節。建議爲頻繁出現的消息元素使用[1-15]間的標識符。若是考慮到之後可能或擴展頻繁元素,能夠預留一些標識符。objective-c

最小的標識符能夠從1開始,最大到229 - 1,或536,870,911。不可使用[19000-19999]之間的標識符, Protobuf協議實現中預留了這些標識符。在.proto文件中使用這些預留標識號,編譯時就會報錯。數組

字段規則

  • repeated:標識字段能夠重複任意次,相似數組ruby

  • proto3不支持proto2中的required和optionalide

添加更多message類型

一個.proto文件中能夠定義多個消息類型,通常用於同時定義多個相關的消息,例如在同一個.proto文件中同時定義搜索請求和響應消息:ui

syntax = "proto3";

// SearchRequest 搜索請求
message SearchRequest {
    string query = 1;           // 查詢字符串
    int32  page_number = 2;     // 頁碼
    int32  result_per_page = 3; // 每頁條數
}

// SearchResponse 搜索響應
message SearchResponse {
    ...
}

添加註釋

向.proto文件中添加註釋,支持C風格雙斜線//單行註釋

保留字段與標識符

可使用reserved關鍵字指定保留字段和保留標識符:

message Foo {
    reserved 2, 15, 9 to 11;
    reserved "foo", "bar";
}

注意,不能在一個reserved聲明中混合字段名和標識符。

.proto文件編譯結果

當使用protocol buffer編譯器運行.proto文件時,編譯器將生成所選語言的代碼,用於使用在.proto文件中定義的消息類型、服務接口約定等。不一樣語言生成的代碼格式不一樣:

  • C++: 每一個.proto文件生成一個.h文件和一個.cc文件,每一個消息類型對應一個類

  • Java: 生成一個.java文件,一樣每一個消息對應一個類,同時還有一個特殊的Builder類用於建立消息接口

  • Python: 姿式不太同樣,每一個.proto文件中的消息類型生成一個含有靜態描述符的模塊,該模塊與一個元類metaclass在運行時建立須要的Python數據訪問類

  • Go: 生成一個.pb.go文件,每一個消息類型對應一個結構體

  • Ruby: 生成一個.rb文件的Ruby模塊,包含全部消息類型

  • JavaNano: 相似Java,但不包含Builder

  • Objective-C: 每一個.proto文件生成一個pbobjc.h和一個pbobjc.m文件

  • C#: 生成.cs文件包含,每一個消息類型對應一個類

各類語言的更多的使用方法請參考官方API文檔

數據類型

這裏直接引用官方文檔的描述:

.proto C++ Java Python Go Ruby C#
double double double float float64 Float double
float float float float float32 Float float
int32 int32 int int int32 Fixnum or Bignum int
int64 int64 long ing/long[3] int64 Bignum long
uint32 uint32 int[1] int/long[3] uint32 Fixnum or Bignum uint
uint64 uint64 long[1] int/long[3] uint64 Bignum ulong
sint32 int32 int intj int32 Fixnum or Bignum int
sint64 int64 long int/long[3] int64 Bignum long
fixed32 uint32 int[1] int uint32 Fixnum or Bignum uint
fixed64 uint64 long[1] int/long[3] uint64 Bignum ulong
sfixed32 int32 int int int32 Fixnum or Bignum int
sfixed64 int64 long int/long[3] int64 Bignum long
bool bool boolean boolean bool TrueClass/FalseClass bool
string string String str/unicode[4] string String(UTF-8) string
bytes string ByteString str []byte String(ASCII-8BIT) ByteString

關於這些類型在序列化時的編碼規則請參考 Protocol Buffer Encoding.

[1] java

[2] all

[3] 64

[4] Python

默認值

  • 字符串類型默認爲空字符串

  • 字節類型默認爲空字節

  • 布爾類型默認false

  • 數值類型默認爲0值

  • enums類型默認爲第一個定義的枚舉值,必須是0

針對不一樣語言的默認值的具體行爲參考 generated code guide

枚舉(Enum) TODO

使用其它Message

message SearchResponse {
    repeated Result results = 1;
}

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

message支持嵌套使用,做爲另外一message中的字段類型

導入定義(import)

可使用import語句導入使用其它描述文件中聲明的類型

import "others.proto";

protocol buffer編譯器會在 -I / --proto_path參數指定的目錄中查找導入的文件,若是沒有指定該參數,默認在當前目錄中查找。

Message嵌套

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

內部聲明的message類型名稱只可在內部直接使用,在外部引用須要前置父級message名稱,如Parent.Type

message SomeOtherMessage {
    SearchResponse.Result result = 1;
}

支持多層嵌套:

message Outer {                // Level 0
    message MiddleAA {         // Level 1
        message Inner {        // Level 2
            int64 ival = 1;
            bool  booly = 2;
        }
    }
    message MiddleBB {         // Level 1
        message Inner {        // Level 2
            int32 ival = 1;
            bool  booly = 2;
        }
    }
}

Message更新 TODO

Map類型

proto3支持map類型聲明:

map<key_type, value_type> map_field = N;

message Project {...}
map<string, Project> projects = 1;
  • 鍵、值類型能夠是內置的標量類型,也能夠是自定義message類型

  • 字段不支持repeated屬性

  • 不要依賴map類型的字段順序

包(Packages)

.proto文件中使用package聲明包名,避免命名衝突。

syntax = "proto3";
package foo.bar;
message Open {...}

在其餘的消息格式定義中可使用包名+消息名的方式來使用類型,如:

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

在不一樣的語言中,包名定義對編譯後生成的代碼的影響不一樣:

  • C++ 中:對應C++命名空間,例如Open會在命名空間foo::bar

  • Java 中:package會做爲Java包名,除非指定了option jave_package選項

  • Python 中:package被忽略

  • Go 中:默認使用package名做爲包名,除非指定了option go_package選項

  • JavaNano 中:同Java

  • C# 中:package會轉換爲駝峯式命名空間,如Foo.Bar,除非指定了option csharp_namespace選項

定義服務(Service)

若是想要將消息類型用在RPC(遠程方法調用)系統中,能夠在.proto文件中定義一個RPC服務接口,protocol buffer編譯器會根據所選擇的不一樣語言生成服務接口代碼。例如,想要定義一個RPC服務並具備一個方法,該方法接收SearchRequest並返回一個SearchResponse,此時能夠在.proto文件中進行以下定義:

service SearchService {
    rpc Search (SearchRequest) returns (SearchResponse) {}
}

生成的接口代碼做爲客戶端與服務端的約定,服務端必須實現定義的全部接口方法,客戶端直接調用同名方法向服務端發起請求。比較蛋疼的是即使業務上不須要參數也必須指定一個請求消息,通常會定義一個空message。

選項(Options)

在定義.proto文件時能夠標註一系列的options。Options並不改變整個文件聲明的含義,但卻能夠影響特定環境下處理方式。完整的可用選項能夠查看google/protobuf/descriptor.proto.

一些選項是文件級別的,意味着它能夠做用於頂層做用域,不包含在任何消息內部、enum或服務定義中。一些選項是消息級別的,能夠用在消息定義的內部。固然有些選項能夠做用在字段、enum類型、enum值、服務類型及服務方法中。可是到目前爲止,並無一種有效的選項能做用於這些類型。

一下是一些經常使用的選擇:

  • java_package (file option):指定生成java類所在的包,若是在.proto文件中沒有明確的聲明java_package,會使用默認包名。不須要生成java代碼時不起做用

  • java_outer_classname (file option):指定生成Java類的名稱,若是在.proto文件中沒有明確聲明java_outer_classname,生成的class名稱將會根據.proto文件的名稱採用駝峯式的命名方式進行生成。如(foo_bar.proto生成的java類名爲FooBar.java),不須要生成java代碼時不起任何做用

  • objc_class_prefix (file option): 指定Objective-C類前綴,會前置在全部類和枚舉類型名以前。沒有默認值,應該使用3-5個大寫字母。注意全部2個字母的前綴是Apple保留的。

基本規範

描述文件以.proto作爲文件後綴,除結構定義外的語句以分號結尾

  • 結構定義包括:message、service、enum

  • rpc方法定義結尾的分號無關緊要

Message命名採用駝峯命名方式,字段命名採用小寫字母加下劃線分隔方式

message SongServerRequest {
    required string song_name = 1;
}

Enums類型名採用駝峯命名方式,字段命名採用大寫字母加下劃線分隔方式

enum Foo {
    FIRST_VALUE = 1;
    SECOND_VALUE = 2;
}

Service與rpc方法名統一採用駝峯式命名

詳解Go語言編譯結果 TODO

message對應golang中的struct,編譯生成go代碼後,字段名會轉換爲駝峯式

編譯

經過定義好的.proto文件生成Java, Python, C++, Go, Ruby, JavaNano, Objective-C, or C# 代碼,須要安裝編譯器protoc。參考Github項目google/protobuf安裝編譯器.Go語言須要同時安裝一個特殊的插件:golang/protobuf

運行命令:

protoc --proto_path=IMPORT_PATH --cpp_out=DST_DIR --java_out=DST_DIR --python_out=DST_DIR --go_out=DST_DIR --ruby_out=DST_DIR --javanano_out=DST_DIR --objc_out=DST_DIR --csharp_out=DST_DIR path/to/file.proto

這裏只作參考就好,具體語言的編譯實例請參考詳細文檔,其中,Go語言的使用姿式會在其它章節詳細說明:

吐槽: 照着官方文檔一步步操做不必定成功哦!

更多

  • Any 消息類型

  • Oneof 字段

  • 自定義Options

這些用法在實踐中不多使用,這裏不作詳細介紹,尤爲自定義選項設計高級用法,有須要請參考官方文檔

參考

本系列示例代碼

相關文章
相關標籤/搜索