Protocol Buffers 3 語法
標籤(空格分隔): protocol buffers
本主題介紹如何在項目中使用協議緩衝區版本 3
官網地址:https://protobuf.dev/programming-guides/proto3/
定義消息類型
首先,讓我們看一個非常簡單的例子。假設您要定義一種搜索請求消息格式,其中每個搜索請求都有一個查詢字符串、您感興趣的特定結果頁面以及每頁的結果數。下面是用於定義消息類型的 .proto 文件
syntax = "proto3";
message SearchRequest {
string query = 1;
int32 page_number = 2;
int32 results_per_page = 3;
}
- 文件的第一行指定您正在使用 proto3 語法:如果您不這樣做,協議緩衝區編譯器將假定您使用的是 proto2。這必須是文件的第一個非空、非註釋行
- SearchRequest 消息定義指定三個字段(名稱/值對),每個字段對應要包含在此類消息中的每條數據。每個字段都有名稱和類型
指定字段類型
在前面的示例中,所有字段都是標量類型:兩個整數(page_number 和 results_per_page)和一個字符串(查詢)。您還可以指定枚舉和複合類型,例如字段的其他消息類型
-
標量類型官網地址:https://protobuf.dev/programming-guides/proto3/#scalar
double
float
int32
int64
uint32
uint64
sint32
sint64
bool
string
bytes
-
枚舉類型:
enum Corpus {
CORPUS_UNSPECIFIED = 0; // 枚舉類型的編號必須是零開始
CORPUS_UNIVERSAL = 1;
CORPUS_WEB = 2;
CORPUS_IMAGES = 3;
CORPUS_LOCAL = 4;
CORPUS_NEWS = 5;
CORPUS_PRODUCTS = 6;
CORPUS_VIDEO = 7;
}message SearchRequest {
string query = 1;
int32 page_number = 2;
int32 results_per_page = 3;
Corpus corpus = 4;
}
3.其他類型
類型默認值
對於字符串,默認值爲空字符串。
對於字節,默認值爲空字節。
對於布爾值,默認值爲 false。
對於數值類型,默認值爲零。
對於枚舉,默認值是第一個定義的枚舉值,必須爲 0。
對於消息字段,未設置該字段。它的確切值取決於語言。有關詳細信息,請參閱生成的代碼指南
分配字段編號
必須爲消息定義中的每個字段指定一個介於 1 和 536,870,911 之間的數字,並具有以下限制
- 給定的數字在該消息的所有字段中必須是唯一的。
- 字段編號 19,000 到 19,999 保留用於協議緩衝區實現。如果在消息中使用這些保留字段編號之一,協議緩衝區編譯器將進行投訴。
- 不能使用任何以前保留的字段編號或已分配給擴展的任何字段編號。
使用消息類型後,無法更改此數字,因爲它標識消息線格式的字段。“更改”字段編號等效於刪除該字段並創建一個類型相同但新編號的新字段。有關如何正確執行此操作,請參閱刪除字段
切勿重複使用字段編號。切勿從保留列表中取出字段編號,以便在新字段定義中重複使用。請參閱重用字段編號的後果。
對於最常設置的字段,應使用字段編號 1 到 15。較低的字段編號值在導線格式中佔用的空間較少。例如,範圍爲 1 到 15 的字段編號需要一個字節進行編碼。16 到 2047 範圍內的字段編號需要兩個字節。您可以在協議緩衝區編碼中找到有關此內容的更多信息
指定字段標籤
消息字段可以是以下字段之一
-
optional
可選:可選字段處於以下兩種可能狀態之一:- 該字段已設置
- 該字段未設置
-
repeated
重複:此字段類型可以在格式正確的消息中重複零次或多次。將保留重複值的順序 -
map
映射:這是成對的鍵/值字段類型。有關此字段類型的詳細信息,請參閱地圖message Test6 { map<string, int32> g = 7; } message Test6 { message g_Entry { optional string key = 1; optional int32 value = 2; } repeated g_Entry g = 7; }
文件示例
// 選中語法格式proto3,也就是ProtocolBuffer的版本3
syntax = "proto3";
// 定義包名
package proto.v1;
// go語言的包路徑
option go_package="go-example/grpc/proto;example_pb";
//
// 引入官方包
import "google/protobuf/empty.proto";
import "google/protobuf/timestamp.proto";
// 同級目錄導入
import "base.proto";
// 引入不同目錄的proto文件 目前我還沒研究明白
// 1. 官方proto生成例子:會生成兩個pb文件 pb.go && grpc.pb.go
// protoc --go_out=. --go_opt=paths=source_relative --go-grpc_out=. --go-grpc_opt=paths=source_relative ./example.proto
// 2. 生成到一個pb文件:生成到go_package的位置
// protoc --go_out=plugins=grpc:. ./example.proto
// 3. 生成到一個pb文件: 生成到當前目錄
// protoc --go_out=plugins=grpc:. --go_opt=paths=source_relative ./example.proto
// 枚舉類型:編號必須是0開始
enum HouseStatus {
HOUSE_STATUS_0 = 0;
HOUSE_STATUS_1 = 1;
HOUSE_STATUS_2 = 2;
}
// 用戶實體
message UserEntity {
string id = 1;
string username =2;
string avatar = 3;
int64 loginAt = 4;
}
message GetUserRequest {
// 引入同級目錄
Page page = 8;
// 多個data 並且 message 嵌套
repeated UserEntity data = 7;
message Where {
string phone = 1;
string platform = 2;
}
// 枚舉
HouseStatus status = 1;
// 使用其他包的message
google.protobuf.Empty empty = 4;
// map
map<string, string> mp = 5;
// 使用其他包message
google.protobuf.Timestamp addTime = 6;
}
message GetUserInfoRequest {
}
service UserService {
// 一元
rpc GetUserInfoOne(GetUserInfoRequest) returns (UserEntity);
// 服務端流
rpc GetUserInfoTwo(GetUserInfoRequest) returns (stream UserEntity);
// 客戶端流
rpc GetUserInfoThree(stream GetUserInfoRequest) returns (UserEntity);
// 雙向流
rpc GetUserInfoFour(stream GetUserInfoRequest) returns (stream UserEntity);
}
對比Proto和Json編碼
package main
import (
"encoding/json"
"fmt"
"github.com/golang/protobuf/proto"
example_pb "go-example/grpc/proto"
"google.golang.org/protobuf/types/known/timestamppb"
)
func main() {
userRequest := example_pb.GetUserRequest{
Page: &example_pb.Page{
Page: 1,
Limit: 10,
Count: 100,
},
Status: example_pb.HouseStatus_HOUSE_STATUS_0,
Empty: nil,
Mp: map[string]string{
"hello": "world",
},
AddTime: timestamppb.Now(),
}
// 對比proto和json
// proto 編碼
marshal, err := proto.Marshal(&userRequest)
if err != nil {
panic(err)
}
fmt.Printf("proto: %+v\n", marshal)
// json編碼
jsonMarshal, err := json.Marshal(&userRequest)
if err != nil {
panic(err)
}
fmt.Printf("json: %+v\n", jsonMarshal)
}