vlambda博客
学习文章列表

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{
 ...
 }
  1. 文件的第一行指定您正在使用proto3语法:如果不这样做,则协议缓冲区编译器将假定您正在使用proto2。这必须是文件的第一行非空,非注释行。

  1. 分配的字段编号用于标识消息的二进制格式,分配了最好不要修改,如果修改要及时通知使用者

  2. 范围1-15的字段编号需要一个字节来编码,包括字段编码和字段类型,16-2047的字段编号需要占用两个字节

  3. 最小是1 最大2^29 -1 ,保留字段不能使用 19000-19999

分配字段编号

  1. 单数,proto3的默认规则

  2. 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);
 }