热门标签 | HotTags
当前位置:  开发笔记 > 编程语言 > 正文

gRPC(二)入门:Protobuf入门

目录前言一、Protobuf1、什么是protobuf?2、JSON、XML、Protobuf选择1)什么是序列化和反序列化2)JSON、




目录


  • 前言
  • 一、Protobuf
    • 1、什么是protobuf?
    • 2、JSON、XML、Protobuf选择
      • 1)什么是序列化和反序列化
      • 2)JSON、XML、Protobuf对比
      • 3)使用Protobuf替代XML/JSON的好处
      • 4)Protobuf使用场景


  • 二、proto3语法
    • 1、简单示例
    • 2、proto数据类型与Go数据类型对应
    • 3、指定消息字段规则
    • 4、保留标示符 reserved
    • 5、枚举类型
    • 6、引入其他proto文件消息类型
      • 1)被引入文件class.proto
      • 2)使用引入文件user.proto

    • 7、嵌套消息类型
    • 8、map类型消息
    • 9、切片(数组)字段类型
    • 10、oneof(只能选择一个)
    • 11、Any 任何类型

  • 三、小结



前言

通过protubuf文档先了解一下protobuf语法。
个人网站:https://linzyblog.netlify.app/
示例代码已经上传到github:点击跳转



一、Protobuf

1、什么是protobuf?

Protocol Buffers ( Protobuf ) 是一种免费的开源 跨平台数据格式,用于序列化结构化数据。它是谷歌公司开发的一种数据描述语言,并于2008年开源。Protobuf刚开源时的定位类似于XML、JSON等数据描述语言,通过附带工具生成代码并实现将结构化数据序列化的功能。

Protocol Buffers 是一种与语言、平台无关,可扩展的序列化结构化数据的方法,常用于通信协议,数据存储等等。相较于 JSON、XML,它更小、更快、更简单,因此也更受开发人员的青眯。

protobuf官方文档:点击跳转


2、JSON、XML、Protobuf选择


1)什么是序列化和反序列化


  • 序列化是将数据结构或对象状态转换为格式(xml/json/protobuf)的过程可以存储或传输。
  • 反序列化是从表示的格式(xml/json/protobuf)构造数据结构/对象状态的过程

在这里插入图片描述


2)JSON、XML、Protobuf对比

JSON:最流行的主要还是json。因为浏览器对于json数据支持非常好,有很多内建的函数支持。


  • 具有可读性/可编辑性
  • 无需预先知道模式即可解析
  • 优秀的浏览器支持
  • 比 XML 更简洁

JSON数据格式:

{
"title":"Protobuf article",
"status":"DRAFT",
"members" : [
{
"name" : "Molecule Man",
"age" : 29,
"secretIdentity" : "Dan Jukes",
"powers" : [
"Radiation resistance",
"Turning tiny",
"Radiation blast"
]
},
}

XML:现在基本很少使用XML。json使用了键值对的方式,不仅压缩了一定的数据空间,同时也具有可读性。


  • 具有可读性/可编辑性
  • 无需预先知道模式即可解析
  • SOAP等标准
  • 良好的工具支持(xsd、xslt、sax、dom 等)
  • 相当冗长

XML数据格式:

<medium>
<title>Protobuf articlename>
<status>DRAFTstatus>
medium>

Protobuf&#xff1a;适合高性能&#xff0c;对响应速度有要求的数据传输场景。因为profobuf是二进制数据格式&#xff0c;需要编码和解码。数据本身不具有可读性。因此只有在反序列化之后得到真正可读的数据。


  • 非常密集的数据&#xff08;输出小&#xff09;
  • 在不知道架构的情况下很难稳健地解码&#xff08;数据格式在内部是模棱两可的&#xff0c;需要架构来解释&#xff09;
  • 处理速度非常快
  • 不具有可读性/可编辑性&#xff08;密集的二进制数据&#xff09;

Protobuf数据格式&#xff1a;

##.proto file
message Medium {
required string title &#61; 1;
enum StatusType {
DRAFT &#61; 0;
PUBLISHED &#61; 1;
}

message Status {
required StatusType type &#61; 0[default &#61; DRAFT];
}
required Status status &#61; 2;
}

数据格式数据保存方式可读性/可编辑性解析速度语言支持使用范围
JSON文本一般所有语言文件存储、数据交互
XML文本所有语言文件存储、数据交互
Protobuf二进制不可读所有语言文件存储、数据交互

3&#xff09;使用Protobuf替代XML/JSON的好处


  • 与xml/json相比&#xff0c;Protobuf格式在表示数据结构方面更小、更快。
  • xml/Json以字符串形式交换数据&#xff0c;然后在使用解析器检索时解析它们&#xff0c;这个过程在处理和内存消耗方面可能非常昂贵。但是使用protobuf&#xff0c;它使用预定义模式&#xff0c;使得解析逻辑高效而简单。
  • 解析json字符串、数组和对象需要顺序扫描&#xff0c;这意味着没有元素大小或体头的计数。多层次xml文档也是如此。

当然也不能一味的使用Protobuf&#xff0c;JSON适用的场景远远大于Protobuf&#xff0c;在有些时候Protocol Buffers 仍然沒有 JSON 要来的方便。


  • 与xml/json相比&#xff0c;protobuf的学习曲线略高。
  • 当你的数据是需要别人可读的。
  • 你不打算直接处理接收的数据&#xff0c;而是从数据中取你想要的部分处理。
  • 不想经过特殊处理&#xff0c;直接能从浏览器中解读的。
  • 在web服务还没有准备好将数据模型绑定到特定模式的场景中&#xff0c;protobuf没有多大用处。

4&#xff09;Protobuf使用场景


  • 在考虑将 Protobuf 用于web服务之间的通信(比如不与客户端浏览器解析引擎交互)
  • 当文档大小在MB左右&#xff0c;且数据类型混合时&#xff0c;protobuf将在性能方面优于xml/json&#xff0c;protobuf在网络上对数据的编码和解码速度更快。如果数据是巨大的GB&#xff0c;那么无论选择什么编码技术栈(如protobug/json/xml)&#xff0c;都需要压缩。
  • 在需要双重解码的场景中(比如威胁搜索对同一命令行进行多次解码)&#xff0c;protobuf比JSON要快得多。
  • 当web服务过渡到使用gRPC而不是传统的REST框架时&#xff0c;protobuf是推荐使用的标准。

二、proto3语法

1、简单示例

// 指明当前使用proto3语法&#xff0c;如果不指定&#xff0c;编译器会使用proto2
syntax &#61; "proto3";
// package声明符&#xff0c;用来防止消息类型有命名冲突
package msg;
// 选项信息&#xff0c;对应go的包路径
option go_package &#61; "server/msg";
// message关键字&#xff0c;像go中的结构体
message FirstMsg {
// 类型 字段名 标识号
int32 id &#61; 1;
string name&#61;2;
string age&#61;3;
}

syntax: 用来标记当前使用proto的哪个版本。如果不指定&#xff0c;编译器会使用proto2。
package: 指定包名&#xff0c;用来防止消息类型命名冲突。
option go_package: 选项信息&#xff0c;代表生成后的go代码包路径。在生成 gRPC 代码时&#xff0c;必须指明。
message: 声明消息的关键字&#xff0c;类似Go语言中的struct。

FirstMsg 消息定义指定了三个字段&#xff08;名称/值对&#xff09;&#xff0c;每个字段都有一个名称和一个类型。
定义字段语法格式: 类型 字段名 编号&#xff0c;例如repeated int32 nums &#61; 1;在生成gRPC代码时会自动生成数组[]int32类型。



分配字段编号说明:


  • 消息定义中的每个字段都有一个唯一的编号。这些字段编号用于在 消息二进制格式中标识您的字段&#xff0c;并且在使用消息类型后不应更改。
  • [1, 15]之内的标识号在编码的时候会占用一个字节。[16, 2047]之内的标识号则占用2个字节。
  • 最小的标识号可以从1开始&#xff0c;最大到2^29 - 1, or 536,870,911。不可以使用其中的[19000&#xff0d;19999],因为是预留信息&#xff0c;如果使用&#xff0c;编译时会报错。


2、proto数据类型与Go数据类型对应


.proto TypeGo TypeNotes
doublefloat64
floatfloat32
int32int32使用变长编码。对于负值的效率很低&#xff0c;如果有负值,使用sint32
int64int64使用变长编码。对于负值的效率很低&#xff0c;如果有负值,使用sint64
uint32uint32使用变长编码
uint64uint64使用变长编码
sint32int32使用变长编码&#xff0c;负值时比int32高效的多
sint64int64使用变长编码&#xff0c;有符号的整型值。编码时比通常的int64高效。
fixed32uint32总是4个字节&#xff0c;如果数值比228大的话&#xff0c;这个类型会比uint32高效。
fixed64uint64总是8个字节&#xff0c;如果数值比256大的话&#xff0c;这个类型会比uint64高效。
boolbool
stringstring字符串必须包含UTF-8编码或7位ASCII文本&#xff0c;且长度不能超过232
bytes[]byte可以包含不超过232的任意字节序列。

3、指定消息字段规则


  • singular&#xff1a;消息中至多存在一个该字段的数据。使用 proto3 语法时&#xff0c;当没有为给定字段指定其他字段规则时&#xff0c;这是默认字段规则。
  • optional&#xff1a;与 singular 类似&#xff0c;不同之处在于可以检查该值是否已经显式设置了值。字段有两种可能的状态:
    • 该字段已设置&#xff0c;并包含从连接中显式设置或解析的值。它将被序列化到连接上。
    • 该字段未设置&#xff0c;将返回默认值。它不会被序列化。
  • repeated&#xff1a;该字段类型可以在消息中可以重复设置多次&#xff0c;重复值的顺序将被保留。&#xff08;设置成为数组类型&#xff09;
  • map&#xff1a;成对的键/值字段类型。

4、保留标示符 reserved

什么是保留标示符&#xff1f;reserved 标记的编号、字段名&#xff0c;都不能在当前消息中使用。



保留标识符的作用&#xff1a;对于特殊的字段名或者编号通过完全删除字段或将其注释掉来更新消息类型&#xff0c;如果后面出现其他用户对该消息进行更新重用了特殊的字段名或者编号&#xff0c;可能会导致严重的错误&#xff0c;包括数据损坏、出现隐私漏洞等。
为了确保这种情况不会发生的一种方法就是用保留标识符指定保留已删除的字段名或者编号。如果有其他用户试图重用这些字段名或编号&#xff0c;protobuf则会报错预警。


syntax &#61; "proto3";
package demo;
// 在这个消息中标记
message DemoMsg {
// 标示号&#xff1a;1&#xff0c;2&#xff0c;10&#xff0c;11&#xff0c;12&#xff0c;13 都不能用
reserved 1, 2, 10 to 13;
// 字段名 test、name 不能用
reserved "test","name";
// 不能使用字段名&#xff0c;提示:Field name &#39;name&#39; is reserved
string name &#61; 3;
// 不能使用标示号,提示:Field &#39;id&#39; uses reserved number 11
int32 id &#61; 11;
}
// 另外一个消息还是可以正常使用
message Demo2Msg {
// 标示号可以正常使用
int32 id &#61; 1;
// 字段名可以正常使用
string name &#61; 2;
}


注意&#xff1a;不能在同一 reserved 语句中混合字段名称和字段编号。



5、枚举类型

枚举&#xff1a;在定义消息类型时&#xff0c;希望其中一个字段只是预定义值列表中的一个值。
例如&#xff0c;假设您想为每个SearchRequest添加一个Corpus 字段&#xff0c;其中枚举预定义值可以是UNIVERSAL、WEB、IMAGES、LOCAL、NEWS、PRODUCTS或VIDEO。您可以通过在消息定义中添加一个枚举&#xff0c;为每个可能的值添加一个常量。

在下面的示例中&#xff0c;我们添加了一个包含所有可能值的 enum 调用Corpus&#xff0c;以及一个 type 字段Corpus&#xff1a;

enum Corpus {
CORPUS_UNSPECIFIED &#61; 0;
CORPUS_UNIVERSAL &#61; 1;
CORPUS_WEB &#61; 2;
CORPUS_IMAGES &#61; 3;
CORPUS_LOCAL &#61; 4;
CORPUS_NEWS &#61; 5;
CORPUS_PRODUCTS &#61; 6;
CORPUS_VIDEO &#61; 7;
}
message SearchRequest {
string query &#61; 1;
int32 page_number &#61; 2;
int32 result_per_page &#61; 3;
Corpus corpus &#61; 4;
}


每个枚举类型必须将其第一个类型映射为编号0, 原因有两个&#xff1a;


  • 必须有一个零值&#xff0c;以便我们可以使用 0 作为数字 默认值。
  • 零值必须是第一个元素&#xff0c;以便与第一个枚举值始终为默认值的proto2语义兼容 。

可以对相同的编号分配给不同的枚举常量来定义别名。只需要将allow_alias选项设置为true&#xff0c;否则协议编译器将在找到别名时生成错误消息。尽管所有别名值在反序列化期间都有效&#xff0c;但在序列化时始终使用第一个值。

enum EnumAllowingAlias {
option allow_alias &#61; true;
EAA_UNSPECIFIED &#61; 0;
EAA_STARTED &#61; 1;
EAA_RUNNING &#61; 1;
EAA_FINISHED &#61; 2;
}
enum EnumNotAllowingAlias {
ENAA_UNSPECIFIED &#61; 0;
ENAA_STARTED &#61; 1;
// ENAA_RUNNING &#61; 1; // Uncommenting this line will cause a compile error inside Google and a warning message outside.
ENAA_FINISHED &#61; 2;
}


注意&#xff1a;


  • 枚举器常量必须在 32 位整数范围内。
  • 枚举类型同样可以使用保留标识符。


6、引入其他proto文件消息类型


1&#xff09;被引入文件class.proto

文件位置:proto/class.proto

syntax&#61;"proto3";
// 包名
package dto;
// 生成go后的文件路径
option go_package &#61; "grpc/server/dto";
message ClassMsg {
int32 classId &#61; 1;
string className &#61; 2;
}

2&#xff09;使用引入文件user.proto

文件位置:proto/user.proto

syntax &#61; "proto3";
// 导入其他proto文件
import "proto/class.proto";
option go_package&#61;"grpc/server/dto";
package dto;
// 用户信息
message UserDetail{
int32 id &#61; 1;
string name &#61; 2;
string address &#61; 3;
repeated string likes &#61; 4;
// 所属班级
ClassMsg classInfo &#61; 5;
}

如果Goland提示:Cannot resolve import...
在这里插入图片描述


7、嵌套消息类型

可以使用其他消息类型作为字段类型&#xff0c;也可以在其他消息类型中定义和使用消息类型。

syntax &#61; "proto3";
option go_package &#61; "server/nested";
// 学员信息
message UserInfo {
int32 userId &#61; 1;
string userName &#61; 2;
}
message Common {
// 班级信息
message CLassInfo{
int32 classId &#61; 1;
string className &#61; 2;
}
}
// 嵌套信息
message NestedDemoMsg {
// 学员信息 (直接使用消息类型)
UserInfo userInfo &#61; 1;
// 班级信息 (通过Parent.Type&#xff0c;调某个消息类型的子类型)
Common.CLassInfo classInfo &#61;2;
}

8、map类型消息

创建关联映射作为数据定义的一部分&#xff0c;map数据结构格式&#xff1a;

map<key_type, value_type> map_field &#61; N;


注意&#xff1a;


  • key_type只能是任何整数或字符串类型(除浮点类型和任何标量bytes类型)。
  • enum 不能作为key_type和value_type定义的类型。
  • map字段不能是repeated。

示例&#xff1a;

//protobuf源码
syntax &#61; "proto3";
option go_package &#61; "server/demo";
// map消息
message DemoMapMsg {
int32 userId &#61; 1;
map<string,string> like &#61;2;
}
//生成Go代码
type DemoMapMsg struct {
state protoimpl.MessageState
sizeCache protoimpl.SizeCache
unknownFields protoimpl.UnknownFields
UserId int32 &#96;protobuf:"varint,1,opt,name&#61;userId,proto3" json:"userId,omitempty"&#96;
Like map[string]string &#96;protobuf:"bytes,2,rep,name&#61;like,proto3" json:"like,omitempty" protobuf_key:"bytes,1,opt,name&#61;key,proto3" protobuf_val:"bytes,2,opt,name&#61;value,proto3"&#96;
}

向后兼容性
map 语法等效于以下内容&#xff0c;因此不支持 map 的Protobuf实现仍然可以处理你的数据&#xff1a;

message MapFieldEntry {
key_type key &#61; 1;
value_type value &#61; 2;
}
repeated MapFieldEntry map_field &#61; N;

任何支持映射的Protobuf实现都必须生成和接受上述定义可以接受的数据。


9、切片(数组)字段类型

需要创建切片(数组)字段类型&#xff1a;

//protobuf源码
syntax &#61; "proto3";
option go_package &#61; "server/demo";
// repeated允许字段重复&#xff0c;对于Go语言来说&#xff0c;它会编译成数组(slice of type)类型的格式
message DemoSliceMsg {
// 会生成 []int32
repeated int32 id &#61; 1;
// 会生成 []string
repeated string name &#61; 2;
// 会生成 []float32
repeated float price &#61; 3;
// 会生成 []float64
repeated double money &#61; 4;
}
//生成Go代码
// repeated允许字段重复&#xff0c;对于Go语言来说&#xff0c;它会编译成数组(slice of type)类型的格式
type DemoSliceMsg struct {
state protoimpl.MessageState
sizeCache protoimpl.SizeCache
unknownFields protoimpl.UnknownFields
// 会生成 []int32
Id []int32 &#96;protobuf:"varint,1,rep,packed,name&#61;id,proto3" json:"id,omitempty"&#96;
// 会生成 []string
Name []string &#96;protobuf:"bytes,2,rep,name&#61;name,proto3" json:"name,omitempty"&#96;
// 会生成 []float32
Price []float32 &#96;protobuf:"fixed32,3,rep,packed,name&#61;price,proto3" json:"price,omitempty"&#96;
Money []float64 &#96;protobuf:"fixed64,4,rep,packed,name&#61;money,proto3" json:"money,omitempty"&#96;
}

10、oneof(只能选择一个)

如果需要一条包含多个字段的消息&#xff0c;并且最多同时设置一个字段&#xff0c;可以强制执行此行为并使用 oneof 功能节省内存。

oneof 字段与常规字段一样&#xff0c;在 oneof 共享内存中的所有字段&#xff0c;最多可以同时设置一个字段。设置 oneof 的任何成员会自动清除所有其他成员。

如果设置了多个值&#xff0c;则由 proto 中的 order 确定的最后一个设置的值将覆盖所有以前的设置值。

message SampleMessage {
oneof test_oneof {
string name &#61; 4;
SubMessage sub_message &#61; 9;
}
}

在生成的代码中&#xff0c;oneof 字段具有与常规字段相同的 getter 和 setter。还可以获得一种特殊的方法来检查 oneof 中设置了哪个值&#xff08;如果有&#xff09;。


11、Any 任何类型

Any消息类型允许您将消息作为嵌入类型使用&#xff0c;而不需要它们的.proto定义。

Any以字节的形式包含任意序列化的消息&#xff0c;以及作为该消息类型的全局唯一标识符并解析为该消息类型的URL。要使用Any类型&#xff0c;您需要 import google/protobuf/any.proto

import "google/protobuf/any.proto";
message ErrorStatus {
string message &#61; 1;
repeated google.protobuf.Any details &#61; 2;
}

三、小结

本章我们了解proto3的语法&#xff0c;下一章我会详细介绍gRPC以及如何加载 protoc-gen-go 插件达到生成 Go 代码的目的。







推荐阅读
  • 本文介绍了数据库的存储结构及其重要性,强调了关系数据库范例中将逻辑存储与物理存储分开的必要性。通过逻辑结构和物理结构的分离,可以实现对物理存储的重新组织和数据库的迁移,而应用程序不会察觉到任何更改。文章还展示了Oracle数据库的逻辑结构和物理结构,并介绍了表空间的概念和作用。 ... [详细]
  • ZSI.generate.Wsdl2PythonError: unsupported local simpleType restriction ... [详细]
  • 推荐系统遇上深度学习(十七)详解推荐系统中的常用评测指标
    原创:石晓文小小挖掘机2018-06-18笔者是一个痴迷于挖掘数据中的价值的学习人,希望在平日的工作学习中,挖掘数据的价值, ... [详细]
  • Ubuntu安装常用软件详细步骤
    目录1.GoogleChrome浏览器2.搜狗拼音输入法3.Pycharm4.Clion5.其他软件1.GoogleChrome浏览器通过直接下载安装GoogleChro ... [详细]
  • Go Cobra命令行工具入门教程
    本文介绍了Go语言实现的命令行工具Cobra的基本概念、安装方法和入门实践。Cobra被广泛应用于各种项目中,如Kubernetes、Hugo和Github CLI等。通过使用Cobra,我们可以快速创建命令行工具,适用于写测试脚本和各种服务的Admin CLI。文章还通过一个简单的demo演示了Cobra的使用方法。 ... [详细]
  • 本文介绍了如何使用JSONObiect和Gson相关方法实现json数据与kotlin对象的相互转换。首先解释了JSON的概念和数据格式,然后详细介绍了相关API,包括JSONObject和Gson的使用方法。接着讲解了如何将json格式的字符串转换为kotlin对象或List,以及如何将kotlin对象转换为json字符串。最后提到了使用Map封装json对象的特殊情况。文章还对JSON和XML进行了比较,指出了JSON的优势和缺点。 ... [详细]
  • .NET最新漏洞CVE20178759 POC已公布,大规模攻击即将来临
    图世界范围内感染FinSpy图(即漏洞利用导致下载的间谍程序)前天微软刚补好的漏洞,昨天fireeye发文,然后各平台开始写 ... [详细]
  • WCF分布式开发常见错误(30):StartelementBinaryexpected(期望的初始元素是Binary).FoundSayHello.调试WCF4.0代 ... [详细]
  • 【从入门到精通】六个步骤助您成为流程云专家流程交互
    Oracle流程云是属于OraclePaaS云中一个比较核心的组成部分,主要用于在云上建立一个企业的流程优化中心,为SaaS应用提供流程定制场景,传统的业务流程迁移到云上实现等场景 ... [详细]
  • 本文介绍了在开发Android新闻App时,搭建本地服务器的步骤。通过使用XAMPP软件,可以一键式搭建起开发环境,包括Apache、MySQL、PHP、PERL。在本地服务器上新建数据库和表,并设置相应的属性。最后,给出了创建new表的SQL语句。这个教程适合初学者参考。 ... [详细]
  • Excel数据处理中的七个查询匹配函数详解
    本文介绍了Excel数据处理中的七个查询匹配函数,以vlookup函数为例进行了详细讲解。通过示例和语法解释,说明了vlookup函数的用法和参数的含义,帮助读者更好地理解和运用查询匹配函数进行数据处理。 ... [详细]
  • 如何用UE4制作2D游戏文档——计算篇
    篇首语:本文由编程笔记#小编为大家整理,主要介绍了如何用UE4制作2D游戏文档——计算篇相关的知识,希望对你有一定的参考价值。 ... [详细]
  • 本文介绍了P1651题目的描述和要求,以及计算能搭建的塔的最大高度的方法。通过动态规划和状压技术,将问题转化为求解差值的问题,并定义了相应的状态。最终得出了计算最大高度的解法。 ... [详细]
  • Node.js学习笔记(一)package.json及cnpm
    本文介绍了Node.js中包的概念,以及如何使用包来统一管理具有相互依赖关系的模块。同时还介绍了NPM(Node Package Manager)的基本介绍和使用方法,以及如何通过NPM下载第三方模块。 ... [详细]
  • PHP面试题实例代码分析
    本篇内容主要讲解“PHP面试题实例代码分析”,感兴趣的朋友不妨来看看。本文介绍的方法操作简单快捷,实用性强。下面就让小编来带大家学习“PHP面 ... [详细]
author-avatar
唐珀虎1979
这个家伙很懒,什么也没留下!
PHP1.CN | 中国最专业的PHP中文社区 | DevBox开发工具箱 | json解析格式化 |PHP资讯 | PHP教程 | 数据库技术 | 服务器技术 | 前端开发技术 | PHP框架 | 开发工具 | 在线工具
Copyright © 1998 - 2020 PHP1.CN. All Rights Reserved | 京公网安备 11010802041100号 | 京ICP备19059560号-4 | PHP1.CN 第一PHP社区 版权所有