grpc从0开始-1-protobuf的认识及使用
结束了http的章节,下面我们一起来看看微服务离不开的grpc是怎么实现负载均衡、接口认证、trace追踪、失败重试、健康检查、映射服务等等,让我们一起成为面试有资可谈
1、什么是RPC
RPC 代指远程过程调用(Remote Procedure Call),它的调用包含了传输协议和编码(对象序列号)协议等等。允许运行于一台计算机的程序调用另一台计算机的子程序,而开发人员无需额外地为这个交互作用编程
2、Protobuf
Protocol Buffers 是一种与语言、平台无关,可扩展的序列化结构化数据的方法,常用于通信协议,数据存储等等。相较于 JSON、XML,它更小、更快、更简单
1、语法
syntax ="proto3";
service SearchService{
rpc Search(SearchRequest) returns (SearchResponse);
}
message SearchRequest{
string query =1;
int32 page_number =2;
int32 result_per_page =3;
}
message SearchResponse{
...
}
文件的第一行指定您正在使用
proto3
语法:如果不这样做,则协议缓冲区编译器将假定您正在使用proto2。这必须是文件的第一行非空,非注释行。
分配的字段编号用于标识消息的二进制格式,分配了最好不要修改,如果修改要及时通知使用者
范围1-15的字段编号需要一个字节来编码,包括字段编码和字段类型,16-2047的字段编号需要占用两个字节
最小是1 最大2^29 -1 ,保留字段不能使用 19000-19999
分配字段编号
单数,proto3的默认规则
repeated 可以重复任意次
注释
支持单行注释//
多行注释/**/
保留字段reserved
当定义好字段后, 在后续开发中发现某个字段根本没用.
例如 string userName = 2;
字段, 这个时候最好不要进行注释或删除.
有可能以后加载相同的旧版本, 这可能会导致数据损坏, 隐私错误等. 确保不会发生这种情况的一种方法是指定要删除的字段为保留字段.
message SubscribeReq {
reserved 2;
int32 subReqID = 1;
string userName = 2;
string productName = 3;
string address = 4;
}
或者
message Foo {
reserved 2, 15, 9 to 11;
reserved "foo", "bar";
}
顾名思义, 就是此字段会被保留可能在以后会使用此字段. 使用关键字 reserved
表示要保留字段编号为 2
.
基本类型对照表
.proto Type | Notes | C++ Type | Java/Kotlin Type[1] | Python Type[3] | Go Type | Ruby Type | C# Type | PHP Type | Dart Type |
---|---|---|---|---|---|---|---|---|---|
double | double | double | float | float64 | Float | double | float | double | |
float | float | float | float | float32 | Float | float | float | double | |
int32 | int32 | int | int | int32 | Fixnum or Bignum (as required) | int | integer | int | |
int64 | int64 | long | int/long[4] | int64 | Bignum | long | integer/string[6] | Int64 | |
uint32 | uint32 | int[2] | int/long[4] | uint32 | Fixnum or Bignum (as required) | uint | integer | int | |
uint64 | uint64 | long[2] | int/long[4] | uint64 | Bignum | ulong | integer/string[6] | Int64 | |
sint32 | int32 | int | int | int32 | Fixnum or Bignum (as required) | int | integer | int | |
sint64 | int64 | long | int/long[4] | int64 | Bignum | long | integer/string[6] | Int64 | |
fixed32 | int[2] | int/long[4] | uint32 | Fixnum or Bignum (as required) | uint | integer | int | ||
fixed64 | uint64 | long[2] | int/long[4] | uint64 | Bignum | ulong | integer/string[6] | Int64 | |
sfixed32 | int32 | int | int | int32 | Fixnum or Bignum (as required) | int | integer | int | |
sfixed64 | int64 | long | int/long[4] | int64 | Bignum | long | integer/string[6] | Int64 | |
bool | bool | boolean | bool | bool | TrueClass/FalseClass | bool | boolean | bool | |
string | String | str/unicode[5] | string | String (UTF-8) | string | string | String | ||
bytes | string | ByteString | str | []byte | String (ASCII-8BIT) | ByteString | string | List |
自定义类型
message SearchResponse {
repeated Result results = 1;
}
message Result {
string url = 1;
string title = 2;
repeated string snippets = 3;
}
Result 为自定义的类型
嵌套类型
message SearchResponse {
message Result {
string url = 1;
string title = 2;
repeated string snippets = 3;
}
repeated Result results = 1;
}
在其他地方调用
message SomeOtherMessage {
SearchResponse.Result result = 1;
}
深度嵌套
message Outer { // Level 0
message MiddleAA { // Level 1
message Inner { // Level 2
int64 ival = 1;
bool booly = 2;
}
}
message MiddleBB { // Level 1
message Inner { // Level 2
int32 ival = 1;
bool booly = 2;
}
}
}
Any类型
该Any
消息类型,可以使用邮件作为嵌入式类型,而不必自己.proto定义。一个Any
含有任意的序列化消息bytes
,以充当一个全局唯一标识符和解析为消息的类型的URL一起。要使用该Any
类型,您需要导入 google/protobuf/any.proto
import "google/protobuf/any.proto";
message ErrorStatus {
string message = 1;
repeated google.protobuf.Any details = 2; //只能使用repeated
}
给定消息类型的默认类型URL是type.googleapis.com/_packagename_._messagename_
。
引入的时候会报错
解决方法:
https://www.cnblogs.com/x-poior/p/9266087.html
直接在当前目录建立层次目录 引入文件内容
https://www.cnblogs.com/ExMan/p/13892736.html
生成pb.go文件
enum类型
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;
}
Corpus
枚举的第一个常量映射为零:每个枚举定义必须包含一个映射为零的常量作为其第一个元素。这是因为:
必须有一个零值,以便我们可以使用0作为数字默认值。
零值必须是第一个元素,以便与proto2语义兼容,其中第一个枚举值始终是默认值。
枚举器常量必须在32位整数范围内。由于
enum
值在电线上使用varint编码,因此负值效率不高,因此不建议使用
map类型
message OneOf {
uint64 Id = 1 ;
string Title = 2;
string Content = 3;
}
message Maps {
map<string,string> dir = 1;
map<int64,int32> dir2 = 2;
map<int64,OneOf> dir3 = 3;
}
相较 Protobuf,为什么不使用 XML?
更简单
数据描述文件只需原来的 1/10 至 1/3
解析速度是原来的 20 倍至 100 倍
减少了二义性
生成了更易使用的数据访问类
3、定义服务
service SearchService {
rpc Search(SearchRequest) returns (SearchResponse);
}