Protoc 语法
本文主要介绍了 Protocol Buffers(简称 proto)的基本数据类型、标量类型、结构定义及高级用法,并结合 Go 语言的实现进行说明。
-
基本数据类型:
定义了
Request消息体,包含多种数据类型字段,如double、float、int32等,并展示了其在 Go 中的映射关系。每种数据类型在不同编程语言中有对应的类型转换规则。 -
标量类型:
列举了 proto 标量类型的特性及适用场景。例如,
int32和uint32使用变长编码,sint32对负值更高效,fixed32和fixed64固定字节长度适合大数值等。未赋值字段在序列化时会被赋予默认值。 -
结构定义:
- message 定义:消息体以大写字母开头,字段名以下划线命名并分配唯一标识。
- 切片(数组):使用
repeated关键字表示数组类型。 - map:支持键值对形式的数据结构。
- 嵌套类型:允许在一个 message 内部定义子 message。
- 字段作废:通过
reserved保留字段编号或名称,避免冲突。 - 枚举类型:必须包含值为 0 的枚举常量,支持别名设置。
-
oneof:
在一个
oneof块中,多个字段共享同一存储空间,设置其中一个字段会清空其他字段。 -
服务定义:
提供四种服务定义方式:
- 即刻响应服务。
- 入参为流的服务。
- 出参为流的服务。
- 入参和出参均为流的服务。
基本数据类型
message Request {
double a1 = 1;
float a2 = 2;
int32 a3 = 3;
uint32 a4 = 4;
uint64 a5 = 5;
sint32 a6 = 6;
sint64 a7 = 7;
fixed32 a8 = 8;
fixed64 a9 = 9;
sfixed32 a10 = 10;
sfixed64 a11 = 11;
bool a12 = 12;
string a13 = 13;
bytes a14 = 14;
}
type Request struct {
state protoimpl.MessageState
sizeCache protoimpl.SizeCache
unknownFields protoimpl.UnknownFields
A1 float64 `protobuf:"fixed64,1,opt,name=a1,proto3" json:"a1,omitempty"`
A2 float32 `protobuf:"fixed32,2,opt,name=a2,proto3" json:"a2,omitempty"`
A3 int32 `protobuf:"varint,3,opt,name=a3,proto3" json:"a3,omitempty"`
A4 uint32 `protobuf:"varint,4,opt,name=a4,proto3" json:"a4,omitempty"`
A5 uint64 `protobuf:"varint,5,opt,name=a5,proto3" json:"a5,omitempty"`
A6 int32 `protobuf:"zigzag32,6,opt,name=a6,proto3" json:"a6,omitempty"`
A7 int64 `protobuf:"zigzag64,7,opt,name=a7,proto3" json:"a7,omitempty"`
A8 uint32 `protobuf:"fixed32,8,opt,name=a8,proto3" json:"a8,omitempty"`
A9 uint64 `protobuf:"fixed64,9,opt,name=a9,proto3" json:"a9,omitempty"`
A10 int32 `protobuf:"fixed32,10,opt,name=a10,proto3" json:"a10,omitempty"`
A11 int64 `protobuf:"fixed64,11,opt,name=a11,proto3" json:"a11,omitempty"`
A12 bool `protobuf:"varint,12,opt,name=a12,proto3" json:"a12,omitempty"`
A13 string `protobuf:"bytes,13,opt,name=a13,proto3" json:"a13,omitempty"`
A14 []byte `protobuf:"bytes,14,opt,name=a14,proto3" json:"a14,omitempty"
}
proto 各语言类型转换
| Proto Type | C++ Type | Java/Kotlin Type\[1] | Python Type\[3] | Go Type | Ruby Type | C# Type | PHP Type | Dart Type | Rust Type |
|---|---|---|---|---|---|---|---|---|---|
| double | double | double | float | float64 | Float | double | float | double | f64 |
| float | float | float | float | float32 | Float | float | float | double | f32 |
| int32 | int32\_t | int | int | int32 | Fixnum or Bignum (as required) | int | integer | int | i32 |
| int64 | int64\_t | long | int/long\[4] | int64 | Bignum | long | integer/string\[6] | Int64 | i64 |
| uint32 | uint32\_t | int\[2] | int/long\[4] | uint32 | Fixnum or Bignum (as required) | uint | integer | int | u32 |
| uint64 | uint64\_t | long\[2] | int/long\[4] | uint64 | Bignum | ulong | integer/string\[6] | Int64 | u64 |
| sint32 | int32\_t | int | int | int32 | Fixnum or Bignum (as required) | int | integer | int | i32 |
| sint64 | int64\_t | long | int/long\[4] | int64 | Bignum | long | integer/string\[6] | Int64 | i64 |
| fixed32 | uint32\_t | int\[2] | int/long\[4] | uint32 | Fixnum or Bignum (as required) | uint | integer | int | u32 |
| fixed64 | uint64\_t | long\[2] | int/long\[4] | uint64 | Bignum | ulong | integer/string\[6] | Int64 | u64 |
| sfixed32 | int32\_t | int | int | int32 | Fixnum or Bignum (as required) | int | integer | int | i32 |
| sfixed64 | int64\_t | long | int/long\[4] | int64 | Bignum | long | integer/string\[6] | Int64 | i64 |
| bool | bool | boolean | bool | bool | TrueClass/FalseClass | bool | boolean | bool | bool |
| string | std::string | String | str/unicode\[5] | string | String (UTF-8) | string | string | String | ProtoString |
| bytes | std::string | ByteString | str (Python 2), bytes (Python 3) | \[]byte | String (ASCII-8BIT) | ByteString | string | List | ProtoBytes |
标量类型
| .proto Type | 解释 | Go Type |
|---|---|---|
| double | float64 | |
| float | float32 | |
| int32 | 使用变长编码,对于负值的效率很低,如果你的域有可能有负值,请使用sint64替代 | int32 |
| uint32 | 使用变长编码 | uint32 |
| uint64 | 使用变长编码 | uint64 |
| sint32 | 使用变长编码,这些编码在负值时比int32高效的多 | int32 |
| sint64 | 使用变长编码,有符号的整型值。编码时比通常的int64高效 | int64 |
| fixed32 | 总是4个字节,如果数值总是比总是比228大的话,这个类型会比uint32高效。 | uint32 |
| fixed64 | 总是8个字节,如果数值总是比总是比256大的话,这个类型会比uint64高效。 | uint64 |
| sfixed32 | 总是4个字节 | int32 |
| sfixed64 | 总是8个字节 | int64 |
| bool | bool | |
| string | 一个字符串必须是UTF-8编码或者7-bit ASCII编码的文本 | string |
| bytes | 可能包含任意顺序的字节数据 | \[]byte |
当标量类型没有被赋值时,是不会被序列化的,解析时就会赋予默认值。 strings:空字符串,bytes:空序列,bools:false,数值类型:0
message 定义结构
message 名称(大写开头) { 类型 key(下划线) = 唯一(标识) }
message Req{
string user_name = 1;
}
切片(数组)
message ArrayRequest {
repeated int64 a1 = 1;
repeated string a2 = 2;
repeated Request request_list = 3;
}
ps:需要添加关键字 repeated
type ArrayRequest struct {
A1 []int64
A2 []string
RequestList []*Request
}
map
map<key 类型,value 类型> key = 标识;
message MapRequest {
map<int64, string> m_i_s = 1;
map<string, bool> m_i_b = 2;
map<string, ArrayRequest> m_i_arr = 3;
}
type MapRequest struct {
MIS map[int64]string
MIB map[string]bool
MIArr map[string]*ArrayRequest
}
类型嵌套
message Req{
message Person{
string name = 1;
}
int32 id = 1;
Person person = 2;
}
字段作废
通过reserved 实现字段作废(保留)
message Req{
string user_name = 1;
string one = 2;
string two = 3;
string three = 4;
string four = 5;
string five = 6;
reserved 2,4 to 6;
reserved "one","five";
}
枚举类型
一定要存在一个枚举常量值为 0,不然不合法。
message SearchRequest {
string query = 1;
int32 page_number = 2;
int32 result_per_page = 3;
enum Corpus {
UNIVERSAL = 0;
WEB = 1;
IMAGES = 2;
LOCAL = 3;
NEWS = 4;
PRODUCTS = 5;
VIDEO = 6;
}
Corpus corpus = 4;
}
ps:当你的枚举里面存在相同常量值的时候 必须设置这个选项
options allow_alias = true;
oneof
我们可以再message里用 oneof 来包裹某个段落。在这个作用区域中,所有的值只要被设置过了,其他的值会被清空
message Response {
oneof response_type {
User user = 1;
Error error = 2;
EmptyResponse empty = 3;
}
}
四种定义服务
1.传统的 即刻响应的
service SearchService {
rpc Search(SearchRequest) returns (SearchResponse);
}
2.入参为流
rpc SearchIN(stream PersonReq) returns (PersonRes);
3.出参为流
rpc SearchOUT(PersonReq) returns (stream PersonRes);
4.入参、出参皆为流
rpc SearchIO(stream PersonReq) returns (stream PersonRes);