一文带你玩转 ProtoBuf
前言
在网络通信和通用数据交换等应用场景中经常使用的技术是 JSON 或 XML,在微服务架构中通常使用另外一个数据交换的协议的工具 ProtoBuf。
ProtoBuf 也是我们做微服务开发,进行 Go 进阶实战中,必知必会的知道点。
今天就开始第一章内容:《一文带你玩转 ProtoBuf》
5 分钟入门
1.1 简介
你可能不知道 ProtoBuf,但一定知道 json 或者 xml,从一定意义上来说他们的作用是一样的。
ProtoBuf 全称:protocol buffers,直译过来是:“协议缓冲区”,是一种与语言无关、与平台无关的可扩展机制,用于序列化结构化数据。
和 json\xml 最大的区别是:json\xml 都是基于文本格式,ProtoBuf 是二进制格式。
ProtoBuf 相比于 json\XML,更小(3 ~ 10 倍)、更快(20 ~ 100 倍)、更为简单。
我们只需要定义一次数据结构,就可以使用 ProtoBuf 生成源代码,轻松搞定在各种数据流和各种语言中写入、读取结构化数据。
1.2 安装
建议大家使用主流版本 v3,这是官网下载地址:https://github.com/protocolbuffers/ProtoBuf/releases
注意,不同的电脑系统安装包是不一样的:
小技巧:Mac 查看自己的芯片类型点击左上角的苹果图标,再点击关于本机,就可以查看了。
比如,我的处理器芯片是 intel 的,下载安装包之后是这样的:
bin 目录下的protoc
是 ProtoBuf 的工具集,下文会重点介绍它的使用。
注意:我们需要将下载得到的可执行文件protoc
所在的 bin 目录加到我们电脑的环境变量中。
Mac 安装小技巧
如果你的 Mac 安装了 brew,安装 ProtoBuf 就更简单了,我们使用brew install ProtoBuf
就可以了
1.3 编译 go 语言的工具包
这个 protoc 可以将 proto 文件编译为任何语言的文件,想要编译为 go 语言的,还需要下载另外一个可执行文件
命令是这样的:
1.4 编写 proto 代码
下面就编写一个非常简单,但是五脏齐全的 proto 代码,我们再根据这段代码生成 pb.go 文件。
1.5 生成 go 代码
生成 go 代码,非常简单,使用下面的命令就可以了。
切换到.proto 文件所在目录
指定 proto 源文件,自动生成代码。
执行上面的命令后,我们在项目中就自动生成了一个.pb.go
的文件
入门 ProtoBuf 就是这么的简单:通过这几步我们就完成了 ProtoBuf 的下载、安装、编写了一个 proto 文件,并生成了能用 Go 语言读写 ProtoBuf 的源代码。
我们再深入了解一下 probuf 的用法:
10 分钟进阶
下面再带大家深入了解一下 ProtoBuf 的知识点,避免在开发中踩坑。
小技巧:写 proto 和写 go 最大的区别是需要在结尾添加分号的
;
,在开发过程中给自己提个醒:如果是写 proto 需要加分号,如果是写 go 不需要加分号。
以我们上面的 proto 入门代码举例:
1.1 关键字
syntax:是必须写的,而且要定义在第一行;目前 proto3 是主流,不写默认使用 proto2
package:定义我们 proto 文件的包名
option go_package:定义生成的 pb.go 的包名,我们通常在 proto 文件中定义。如果不在 proto 文件中定义,也可以在使用 protoc 生成代码时指定 pb.go 文件的包名
message:非常重要,用于定义消息结构体,不用着急,下文会重点讲解
细心的小伙伴一定注意到了 message 消息体中有一个 “repeated” 关键字,这在我们写 Go 的时候是没有的。
这是干什么用的呢?下面来详细解答一下:
1.2 数组类型
关于数组类型,和 Java、Go、PHP 等语言中,定义数据类型不一样。
在 ProtoBuf 消息中定义数组类型,是通过在字段前面增加 repeated 关键词实现,标记当前字段是一个数组。
只要使用 repeated 标记类型定义,就表示数组类型。
我们来举两个例子:
1.整数数组:
下面定义的 arrays 表示 int32 类型的数组
2.字符串数组
下面定义的 names 表示字符串数组
repeated
搞懂了,message
又是干嘛用的呢?
1.3 消息
消息(message),在 ProtoBuf 中指的就是我们要定义的数据结构。类似于 Go 中定义结构体。
message 关键词用法也非常简单:
1. 语法
例子:
定义了一个 Request 消息,这个消息有 3 个字段,query 是字符串类型,page 和 limit 是 int32 类型。
1.4 字段类型
ProtoBuf 支持多种数据类型,例如:string、int32、double、float 等等,我整理了一份 ProtoBuf 和 go 语言的数据类型映射表
1.5 分配标识号
细心的小伙伴可能又有疑问了,上面消息体中的 string query = 1;
这个 1 是什么呢?
这些数字是“分配表示号”:在消息定义中,每个字段后面都有一个唯一的数字,这个就是标识号。
这些标识号的作用是:用来在消息的二进制格式中识别各个字段的,一旦开始使用就不能够再改变。
注意:分配标识号在每个消息内唯一,不同的消息体是可以拥有相同的标识号的。
小技巧:[1,15]之内的标识号在编码的时候会占用一个字节。[16,2047]之内的标识号则占用 2 个字节。所以应该为那些频繁出现的消息元素保留 [1,15]之内的标识号。
1.5.1 保留标识号(Reserved)
小技巧:要为将来有可能添加的、频繁出现的字段预留一些标识号。
我们想保留一些标识号,留给以后用,可以使用下面语法:
如果使用了这些保留的标识号,protocol buffer 编译器无法编译通过,将会输出警告信息。
1.6 将消息编译成各种语言版本的类库
编译器命令格式:
OPTION 是命令的选项, PROTO_FILES 是我们要编译的 proto 消息定义文件,支持多个。
常用的 OPTION 选项:
因为开篇我们就用 Go 举了例子,下面再用 Java 举个例子吧:
在当前目录导出 java 版本的代码,编译 hello.proto 消息,执行效果如下:
下载再带小伙伴们了解一下 ProtoBuf 的进阶知识点吧:枚举类型、消息嵌套和 Map 类型。
1.7 枚举类型
写 Java 的同学枚举一定用的很溜,但是写 Go 的同学可能有点懵了,Go 是不直接支持枚举的,并没有 Enum 关键字。
关注我,后续会详解 Go 枚举相关的知识点,在这篇文章中不做重点介绍。
使用枚举的场景是这样的:
当定义一个消息类型的时候,可能想为一个字段指定“预定义值”中的其中一个值,这时候我们就可以通过枚举实现,比如这种:
运行效果如下:
在实际开发中,我们需要定义很多的 proto,我们如何做到消息的复用呢?
答案就是:“消息嵌套”
1.8 消息嵌套
我们在开发 Java 和 PHP 时,经常嵌套使用类,也可以使用其他类作为自己的成员属性类型;在开发 Go 时经常嵌套使用结构体。
在 ProtoBuf 中同样支持消息嵌套,可以在一个消息中嵌套另外一个消息,字段类型可以是另外一个消息类型。
我们来看下面 3 个经典示例:
1.8.1 引用其他消息类型的用法
1.8.2 消息嵌套
类似类嵌套一样,消息也可以嵌套,比如这样:
1.8.3 import 导入其他 proto 文件定义的消息
我们在实际开发中,通常要定义很多消息,如果都写在一个 proto 文件,是不方便维护的。
小技巧:将消息定义写在不同的 proto 文件中,在需要的时候可以通过 import 导入其他 proto 文件定义的消息。
例子:
创建文件: article.proto
创建文件: list_article.proto
执行效果如下,我们顺利生成了.pb.go 文件:
1.9 map 类型
我们在 Go 语言开发中,最常用的就是切片类型和 map 类型了。
切片类型在 ProtoBuf 中对应的就是 repeated 类型,前面我们已经介绍过了。
再重点介绍一下 map 类型,ProtoBuf 也是支持 map 类型的:
1.9.1 map 语法
语法非常简单和通用,但是有几个问题需要我们注意:
key_type
可以是任何整数或字符串类型(除浮点类型和字节之外的任何标量类型)。注意:枚举不是有效的
key_type
。value_type
可以是除另一个映射之外的任何类型。Map 字段不能使用
repeated
关键字修饰。
1.9.2 map 的例子
我们举个典型的例子:学生的学科和分数就适合用 map 定义:
运行效果如下:
再强调一下
注意:Map 字段是不能使用repeated
关键字修饰。
至此我们已经掌握了 ProtoBuf 的所有知识点,是不是非常简单清晰呢?
下面我们在 Go 项目中实战应用一下 ProtoBuf,从 ProtoBuf 中读取数据,并且转换为我们常用的结构体
5 分钟实战
1. 首先我们定义 proto 文件
我创建了一个 demo 目录,创建了名为study_info.proto
的文件
2. 生成代码
使用命令生成 pb.go 文件:
3.编写 go 文件
编写 go 文件,读取 ProtoBuf 中定义的字段,进行赋值,取值,转成结构体等操作:
proto 编码和解码的操作和 json 是非常像的,都使用“Marshal”和“Unmarshal”关键字。
运行结果如下:
本文总结
ProtoBuf 作为开发微服务必选的数据交换协议,基于二进制传输,比 json/xml 更小,速度更快,使用也非常的简单。
通过这篇文章,我们不仅学会了 ProtoBuf 的入门操作,还使用 Go 语言基于 ProtoBuf 编码解码了数据,进行了实战。
进阶部分带大家了解了 ProtoBuf 如何定义消息、ProtoBuf 和 Go 数据类型的映射、枚举类型如何使用、通过消息嵌套复用代码、使用 map 类型时需要注意的问题和小技巧。
微服务架构成为企业项目的必然选择已是趋势,如果你只会开发单体项目,请关注我,带你一起玩转微服务。
天下难事,必作于易。想都是问题,做才有答案。站着不动,永远是观众。
小伙伴们还想看哪些内容,欢迎在评论区留言。
一起学习,升级打怪
我们搞了一个对学 Go 真正有帮助的群,欢迎加入:
公众号:程序员升级打怪之旅
微信号:wangzhongyang1993
版权声明: 本文为 InfoQ 作者【王中阳Go】的原创文章。
原文链接:【http://xie.infoq.cn/article/e868fef233f6a65e595f34828】。文章转载请联系作者。
评论 (1 条评论)