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)
}
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章