Protocol Buffers 3 語法

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;
}
  1. 文件的第一行指定您正在使用 proto3 語法:如果您不這樣做,協議緩衝區編譯器將假定您使用的是 proto2。這必須是文件的第一個非空、非註釋行
  2. SearchRequest 消息定義指定三個字段(名稱/值對),每個字段對應要包含在此類消息中的每條數據。每個字段都有名稱和類型

指定字段類型

在前面的示例中,所有字段都是標量類型:兩個整數(page_number 和 results_per_page)和一個字符串(查詢)。您還可以指定枚舉和複合類型,例如字段的其他消息類型

  1. 標量類型官網地址:https://protobuf.dev/programming-guides/proto3/#scalar
    double float int32 int64 uint32 uint64 sint32 sint64 bool string bytes

  2. 枚舉類型:

    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 之間的數字,並具有以下限制

  1. 給定的數字在該消息的所有字段中必須是唯一的。
  2. 字段編號 19,000 到 19,999 保留用於協議緩衝區實現。如果在消息中使用這些保留字段編號之一,協議緩衝區編譯器將進行投訴。
  3. 不能使用任何以前保留的字段編號或已分配給擴展的任何字段編號。

使用消息類型後,無法更改此數字,因爲它標識消息線格式的字段。“更改”字段編號等效於刪除該字段並創建一個類型相同但新編號的新字段。有關如何正確執行此操作,請參閱刪除字段

切勿重複使用字段編號。切勿從保留列表中取出字段編號,以便在新字段定義中重複使用。請參閱重用字段編號的後果。

對於最常設置的字段,應使用字段編號 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)
}
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章