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;
}
- 文件的第一行指定您正在使用 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)
}
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 震惊!C++程序真的从main开始吗?99%的程序员都答错了
· 【硬核科普】Trae如何「偷看」你的代码?零基础破解AI编程运行原理
· 单元测试从入门到精通
· 上周热点回顾(3.3-3.9)
· winform 绘制太阳,地球,月球 运作规律