Protocol Buffers 2详解

文章目录

  • proto2
    • 消息
    • 字段
      • 字段类型
          • 标准类型
          • `enum` 枚举 类型
          • `Oneof` 类型
          • `Maps` 类型
          • 高级类型
            • google.type常用类型
            • Wrapper 类型(google/protobuf/wrappers.proto)
      • 字段规则
      • 扩展字段
    • option
      • 自定义option (高级功能)
    • 服务
  • proto3
    • 字段类型
      • 标准类型
      • Any
      • 字段规则
  • 教程(proto3)
    • go教程
      • 示例代码
      • 定义proto文件
        • 声明
        • 消息
        • 服务
    • protobuf编译
      • 编译原理
      • 版本问题
      • protoc 编译器
      • protoc-gen-go 插件

参考:

  • 官网
    https://developers.google.com/protocol-buffers
  • 微软文档
    https://docs.microsoft.com/zh-cn/dotnet/architecture/grpc-for-wcf-developers/protocol-buffers

proto2

消息

  • message定义消息
message 消息名 {
	定义字段
}
  • 嵌套定义
    可以在父消息里面定义嵌套的 消息、枚举 类型
    只能在父消息内使用
message 父消息名 {
	message 子消息名 {
	}
}

字段

字段规则 字段类型 字段名 = 字段编号

字段类型

标准类型

https://developers.google.com/protocol-buffers/docs/proto#scalar

enum 枚举 类型
enmu 枚举名 {
	枚举常量 =;
}
Oneof 类型

Oneof多个成员字段,只用一块内存,最多一个字段有值
使用 case() 或 WhichOneof 方法去获取被赋值的字段,具体使用哪个方法取决于具体的编程语言。

oneof 里面的字段使用的编号和外部的字段编号同级的

message 消息名 {
  // 给一个字段赋值,其他字段就变为空
  oneof oneof名 {
	  字段1 = 1;
	  字段2 = 2;
  }
}

https://forrestsu.github.io/posts/go/golang-pb3-oneof/

Maps 类型
map map_field = N;
高级类型

https://developers.google.com/protocol-buffers/docs/overview#data-types
https://protobuf.dev/reference/protobuf/google.protobuf/

常用以下的包

// 用于实现一些高级的功能,还提供了一些工具和库。如 RPC 通信和分布式系统, grpc.health.v1.Health 服务
package google.protobuf

// 用于存储一些常见的数据类型和数据结构。例如 Date、TimeOfDay、Timestamp、LatLng、Money 等等。
package google.type	
google.type常用类型
google.type.Date:用于表示日期,包括年、月、日三个字段。

google.type.TimeOfDay:用于表示一天中的时间,包括小时、分钟、秒钟和纳秒四个字段。

google.protobuf.Timestamp:用于表示时间戳,包括秒数和纳秒数两个字段。

google.type.LatLng:用于表示地理坐标,包括纬度和经度两个浮点数字段。

google.type.Money:用于表示货币金额,包括金额和货币类型两个字段。

google.type.PostalAddress:用于表示邮政地址,包括国家、省份、城市、邮政编码、街道地址等字段
Wrapper 类型(google/protobuf/wrappers.proto)

Wrapper 类型是 Protobuf 3 中的一个特殊消息类型,用于包装原始类型的值。它允许您将原始类型的值存储在一个消息中,并将其序列化为二进制数据流。
作用 :避免了必须使用默认值来表示缺失值的问题。它也可以用于处理某些类型系统中的空值或缺失值

  • 使用案例
syntax = "proto3";

import "google/protobuf/wrappers.proto";

message MyMessage {
  google.protobuf.Int32Value my_int_field = 1;
}

my_int_field 字段是一个 Int32Value 类型,它包装了一个 int32 类型的值。当您将该消息序列化为二进制数据流时,该字段的值将被正确地编码为一个单一的整数值。在接收方收到消息后,您可以使用 .value() 方法来获取实际的 int32 值。

  • Wrapper 类型在 Protobuf 中的定义如下:
syntax = "proto3";

package google.protobuf;

//使用 Int32Value 消息来包装一个 int32 类型的值,并将其序列化为二进制数据流:
message Int64Value {
  int64 value = 1;
}

message DoubleValue {
  double value = 1;
}

message FloatValue {
  float value = 1;
}

message UInt64Value {
  uint64 value = 1;
}

message Int32Value {
  int32 value = 1;
}

message UInt32Value {
  uint32 value = 1;
}

message BoolValue {
  bool value = 1;
}

message StringValue {
  string value = 1;
}

message BytesValue {
  bytes value = 1;
}

字段规则

required: 必输
optional:可选,零个或一个这个字段(但不能超过一个)。
default默认值

optional int32 字段名 = 编号 [default = 默认值];

repeated:动态数组

reserved 保留字段

//如果将来有任何用户尝试使用这些 保留字段标识符,protocol buffer编译器会警告。
message 消息名 {
	reserved 保留字段
}

扩展字段

message Foo {
  字段1 = 1;
  字段2 = 2;
  // 保留几个扩展字段
  extensions 3 to 5;
}

// 扩展字段
extend Foo {
  字段3 = 3;
  字段4 = 4;
  字段5 = 5;
}

确保两个用户不会使用相同的字段编号将扩展名添加到相同的消息类型,否则可能会导致数据的不一致
标识号产生的规则中应该避开[19000-19999]之间的数字,因为这些已经被Protocol Buffers实现中预留了。

option

文件级选项,消息级别的选项,字段级选项
options 有内置也有自定义的

  • 内置option 有多种类型
    比如 fileOption, fieldOption, methodOption 等等

  • 定义格式
    可以写在枚举类型、枚举值、oneof字段、服务类型、服务方法

// 文件选项
option  option名 = 常量 ;


message Foo {
	// 消息选项
	 option 消息选项名 = 常量;
	// fieldOptions 字段选项
	[ repeated ] type fieldName = fieldNumber [ [ 字段选项名 = 常量 ] ] ;
}

自定义option (高级功能)

选项是由google/protobuf/descriptor.proto 文件中的消息定义的,
通过extend 扩展这些消息 就能定义自己的option

  • 例:
    HttpRule扩展:将 rpc 映射成 http 协议
import "google/protobuf/descriptor.proto";

extend google.protobuf.MethodOptions {
    HttpRule http = 72295728;
}

服务

service 服务名 {
  rpc 方法名(入参消息) returns (出参消息);
}

proto3

字段类型

标准类型

https://developers.google.com/protocol-buffers/docs/proto3#scalar

  • proto2 枚举不能直接在 proto3 语法中使用
  • 枚举类型的第一个字段必须为 0

Any

Any包含任意序列化消息,可以保存任意 proto3 消息,类似于proto2 的扩展消息

import "google/protobuf/any.proto";

message 消息名 {
  repeated google.protobuf.Any 字段名 = 1;
}

字段规则

  • optional可选 改名为 singular单数

  • 移除了required 必输

  • 移除了 default 默认值。proto3 中字段的默认值由系统决定

扩展:

  • 移除了对Extensions扩展的支持,新增了 Any 类型
  • proto3中只支持对于option的extend

解析过程中,对未定义字段的处理

  1. proto2 :保留字段
  2. proto3 :丢弃字段
  3. proto3.5+ :保留

教程(proto3)

https://developers.google.com/protocol-buffers/docs/tutorials

go教程

我们将要使用的示例是一个非常简单的“地址簿”应用程序,它可以在文件中读取和写入人们的联系方式。地址簿中的每个人都有一个姓名、一个 ID、一个电子邮件地址和一个联系电话号码。

使用protocol buffers,您可以编写.proto要存储的数据结构的描述。

示例代码

https://github.com/protocolbuffers/protobuf/tree/main/examples

定义proto文件

要创建“地址簿”应用程序,您需要从 .proto文件开始。
文件中的定义.proto很简单:为要序列化的每个数据结构添加一条消息,然后为消息中的每个字段指定名称和类型。

addressbook.proto

声明
syntax = "proto3";
package tutorial;

import "google/protobuf/timestamp.proto";

option go_package = "github.com/protocolbuffers/protobuf/examples/go/tutorialpb";

  • package
    以包声明开头,这有助于防止不同项目之间的命名冲突。
  • import
    导入其他 proto 文件
  • Options
    go_package
    定义了包路径,此文件生成的所有go代码 package为该go_package路径
消息

消息只是包含一组类型字段的聚合。
许多标准的简单数据类型可用作字段类型,包括bool、int32、 float、double和string。
您还可以通过使用其他消息类型作为字段类型来为您的消息添加更多结构。

message Person {
  string name = 1;
  int32 id = 2;  // Unique ID number for this person.
  string email = 3;
  enum PhoneType {
    MOBILE = 0;
    HOME = 1;
    WORK = 2;
  }
  message PhoneNumber {
    string number = 1;
    PhoneType type = 2;
  }
  repeated PhoneNumber phones = 4;

  google.protobuf.Timestamp last_updated = 5;
}

// Our address book file is just one of these.
message AddressBook {
  repeated Person people = 1;
}

编译成go代码结构体为:

type AddressBook struct {
	People               []*Person
}

type Person struct {
	Name                 string
	Id                   int32
	Email                string                 
	Phones               []*PhoneNumber
	LastUpdated          *timestamppb.Timestamp 
}

type PhoneNumber struct {
	Number               string
	Type                 PhoneType
}
// go语言并没有提供enum的定义,使用const来模拟枚举类型
type PhoneType int32
const (
	MOBILE Person_PhoneType = 0
	HOME   Person_PhoneType = 1
	WORK   Person_PhoneType = 2
)
......
服务


protobuf编译

  • 常用编译命令:
protoc -I proto_path --go_out=xxx

protoc -I proto_path --java_out=xxx

protoc -I proto_path --go-grpc_out=xxx

编译原理

protoc对应语言插件 结合,生成桩代码

命令 对应插件 生成文件
–go_out protoc-gen-go 插件; *.pb.go
–java_out protoc-gen-java 插件; *.java
–go-grpc_out protoc-gen-go-grpc 插件;
–*_out protoc-gen-* 插件;
解析.proto文件
输出
渲染
输出
protoc
.proto文件
原生数据结构
原生数据结构
protoc-gen-* 插件
对应语言模板代码

版本问题

github.com/golang/protobufgoogle.golang.org/protobuf

如果在网上发现有些教程让下载github.com/golang/protobuf, 有些让下载google.golang.org/protobuf。不用懵逼,根据实际项目情况决定,

github.com/golang/protobuf 已弃用 ,建议新代码使用该google.golang.org/protobuf模块。

https://pkg.go.dev/github.com/golang/protobuf
https://pkg.go.dev/google.golang.org/protobuf

protoc 编译器

protoc是protobuf文件(.proto)的编译器,
可结合各种语言的插件 将 .proto 文件编译成 C、C++、Golang、Java、Python、PHP 等多种语言的代码 (包含数据类型定义、调用接口等)。

  • 安装
    1. macOS平台 brew安装:http://google.github.io/proto-lens/installing-protoc.html
      brew install protobuf     ##会拉去最新版本
      
    2. 源码安装:
      git clone https://github.com/protocolbuffers/protobuf
      # v3.6.0+以上版本支持map解析,syntax=2、3消息序列化后是二进制兼容的,用root执行以下命令
      cd protobuf
      git checkout v3.6.1.3
      ./autogen.sh
      ./configure
      make -j8
      make install
      

protoc-gen-go 插件

go语言的编译插件

# 旧
go install github.com/golang/protobuf/protoc-gen-go@latest
# 新
go install google.golang.org/protobuf/cmd/[email protected]

你可能感兴趣的:(中间件,golang,proto)