Golang gRPC實踐 連載三 Protobuf文法

來源:互聯網
上載者:User
這是一個建立於 的文章,其中的資訊可能已經有所發展或是發生改變。

Protobuf文法

gRPC推薦使用proto3,本節只介紹常用文法,更多進階使用姿勢請參考官方文檔

Message定義

一個message類型定義描述了一個請求或相應的訊息格式,可以包含多種類型欄位。例如定義一個搜尋請求的訊息格式,每個請求包含查詢字串、頁碼、每頁數目。

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

首行聲明使用的protobuf版本為proto3

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

欄位型別宣告

所有的欄位需要前置聲明資料類型,上面的樣本指定了兩個數實值型別和一個字串類型。除了基本的標量類型還有複合類型,如枚舉、其它message類型等。

標識符Tags

可以看到,訊息的定義中,每個欄位都有一個唯一的數值型標識符。這些標識符用於識別欄位在訊息中的二進位格式,使用中的類型不應該隨意改動。需要注意的是,[1-15]內的標識在編碼時只佔用一個位元組,包含標識符和欄位類型。[16-2047]之間的標識符佔用2個位元組。建議為頻繁出現的訊息元素使用[1-15]間的標識符。如果考慮到以後可能或擴充頻繁元素,可以預留一些標識符。

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

欄位規則

  • repeated:識別欄位可以重複任意次,類似數組

  • proto3不支援proto2中的required和optional

添加更多message類型

一個.proto檔案中可以定義多個訊息類型,一般用於同時定義多個相關的訊息,例如在同一個.proto檔案中同時定義搜尋請求和響應訊息:

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語言的使用姿勢會在其它章節詳細說明:

  • C++ generated code reference

  • Java generated code reference

  • Python generated code reference

  • Go generated code reference

  • Objective-C generated code reference

  • C# generated code reference

吐槽: 照著官方文檔一步步操作不一定成功哦!

更多

  • Any 訊息類型

  • Oneof 欄位

  • 自訂Options

這些用法在實踐中很少使用,這裡不做詳細介紹,尤其自訂選項設計進階用法,有需要請參考官方文檔。

參考

本系列範例程式碼

  • go-grpc-example

相關文章

聯繫我們

該頁面正文內容均來源於網絡整理,並不代表阿里雲官方的觀點,該頁面所提到的產品和服務也與阿里云無關,如果該頁面內容對您造成了困擾,歡迎寫郵件給我們,收到郵件我們將在5個工作日內處理。

如果您發現本社區中有涉嫌抄襲的內容,歡迎發送郵件至: info-contact@alibabacloud.com 進行舉報並提供相關證據,工作人員會在 5 個工作天內聯絡您,一經查實,本站將立刻刪除涉嫌侵權內容。

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.