前言
在网络通信和通用数据交换等利用场景中常常应用的技术是 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/protocolbu...
留神,不同的电脑系统安装包是不一样的:
- Windows 64位 点这里下载
- Windows 32位 点这里下载
- Mac Intel 64位 点这里下载
- Mac ARM 64位 点这里下载
- Linux 64位 点这里下载
小技巧:Mac查看本人的芯片类型点击左上角的苹果图标,再点击对于本机,就能够查看了。
比方,我的处理器芯片是intel的,下载安装包之后是这样的:
bin目录下的protoc
是ProtoBuf的工具集,下文会重点介绍它的应用。
留神:咱们须要将下载失去的可执行文件protoc
所在的 bin 目录加到咱们电脑的环境变量中。
Mac装置小技巧
如果你的Mac装置了brew,装置ProtoBuf就更简略了,咱们应用brew install ProtoBuf
就能够了
1.3 编译go语言的工具包
这个protoc能够将proto文件编译为任何语言的文件,想要编译为go语言的,还须要下载另外一个可执行文件
命令是这样的:
go install google.golang.org/ProtoBuf/cmd/[email protected]
或者
go get github.com/golang/protobuf/protoc-gen-go
1.4 编写proto代码
上面就编写一个非常简单,然而五脏齐全的proto代码,咱们再依据这段代码生成pb.go文件。
syntax = "proto3";package hello;option go_package = "./;hello";message Say{ int64 id = 1; string hello = 2; repeated string word = 3;}
1.5 生成go代码
生成go代码,非常简单,应用上面的命令就能够了。
切换到.proto文件所在目录
cd proto/demo/
指定proto源文件,主动生成代码。
protoc --go_out=. hello.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类型的数组
message Msg { repeated int32 arrays = 1;}
2.字符串数组
上面定义的names示意字符串数组
message Msg { repeated string names = 1;}
repeated
搞懂了,message
又是干嘛用的呢?
1.3 音讯
音讯(message),在ProtoBuf中指的就是咱们要定义的数据结构。相似于Go中定义构造体。
message关键词用法也非常简单:
1. 语法
syntax = "proto3";message 音讯名 { 音讯体}
例子:
syntax = "proto3"; message Request { string query = 1; int32 page = 2; int32 limit = 3;}
定义了一个Request音讯,这个音讯有3个字段,query是字符串类型,page和limit是int32类型。
1.4 字段类型
ProtoBuf反对多种数据类型,例如:string、int32、double、float等等,我整顿了一份ProtoBuf和go语言的数据类型映射表
.proto Type | Go Type | 应用技巧 |
---|---|---|
double | float64 | 没非凡技巧,记住float对应go的float32,double对应go的float64就能够了 |
float | float32 | 没非凡技巧,记住float对应go的float32,double对应go的float64就能够了 |
int32 | int32 | 应用变长编码,对于负值的效率很低,如果你的域有可能有负值,请应用sint64代替 |
uint32 | uint32 | 应用变长编码 |
uint64 | uint64 | 应用变长编码 |
sint32 | int32 | 应用变长编码,这些编码在负值时比int32高效的多 |
sint64 | int64 | 应用变长编码,有符号的整型值。编码时比通常的int64高效。 |
fixed32 | uint32 | 总是4个字节,如果数值都比228大的话,这个类型会比uint32高效。 |
fixed64 | uint64 | 总是8个字节,如果数值都比256大的话,这个类型会比uint64高效。 |
sfixed32 | int32 | 总是4个字节 |
sfixed64 | int64 | 总是8个字节 |
bool | bool | 严格对应,玩不出其余花色来 |
string | string | 一个字符串必须是UTF-8编码或者7-bit ASCII编码的文本。 |
bytes | []byte | 能够蕴含任意程序的字节数组 |
1.5 调配标识号
仔细的小伙伴可能又有疑难了,下面音讯体中的 string query = 1;
这个1是什么呢?
这些数字是“调配示意号”:在音讯定义中,每个字段前面都有一个惟一的数字,这个就是标识号。
这些标识号的作用是:用来在音讯的二进制格局中辨认各个字段的,一旦开始应用就不可能再扭转。
留神:调配标识号在每个音讯内惟一,不同的音讯体是能够领有雷同的标识号的。
小技巧:[1,15]之内的标识号在编码的时候会占用一个字节。[16,2047]之内的标识号则占用2个字节。所以应该为那些频繁呈现的音讯元素保留 [1,15]之内的标识号。
1.5.1 保留标识号(Reserved)
小技巧:要为未来有可能增加的、频繁呈现的字段预留一些标识号。
咱们想保留一些标识号,留给当前用,能够应用上面语法:
message Test { reserved 2, 5, 7 to 10; // 保留2,5,7到10这些标识号}
如果应用了这些保留的标识号,protocol buffer编译器无奈编译通过,将会输入正告信息。
1.6 将音讯编译成各种语言版本的类库
编译器命令格局:
protoc [OPTION] PROTO_FILES
OPTION是命令的选项, PROTO_FILES是咱们要编译的proto音讯定义文件,反对多个。
罕用的OPTION选项:
--go_out=OUT_DIR 指定代码生成目录,生成 Go 代码 --cpp_out=OUT_DIR 指定代码生成目录,生成 C++ 代码 --csharp_out=OUT_DIR 指定代码生成目录,生成 C# 代码 --java_out=OUT_DIR 指定代码生成目录,生成 java 代码 --js_out=OUT_DIR 指定代码生成目录,生成 javascript 代码 --objc_out=OUT_DIR 指定代码生成目录,生成 Objective C 代码 --php_out=OUT_DIR 指定代码生成目录,生成 php 代码 --python_out=OUT_DIR 指定代码生成目录,生成 python 代码 --ruby_out=OUT_DIR 指定代码生成目录,生成 ruby 代码
因为开篇咱们就用Go举了例子,上面再用Java举个例子吧:
protoc --java_out=. hello.proto
在当前目录导出java版本的代码,编译hello.proto音讯,执行成果如下:
下载再带小伙伴们理解一下ProtoBuf的进阶知识点吧:枚举类型、音讯嵌套和Map类型。
1.7 枚举类型
写Java的同学枚举肯定用的很溜,然而写Go的同学可能有点懵了,Go是不间接反对枚举的,并没有Enum关键字。
关注我,后续会详解Go枚举相干的知识点,在这篇文章中不做重点介绍。
应用枚举的场景是这样的:
当定义一个音讯类型的时候,可能想为一个字段指定“预约义值”中的其中一个值,这时候咱们就能够通过枚举实现,比方这种:
syntax = "proto3";//指定版本信息,非正文的第一行enum SexType //枚举音讯类型,应用enum关键词定义,一个性别类型的枚举类型{ UNKONW = 0; //proto3版本中,首成员必须为0,成员不应有雷同的值 MALE = 1; //1男 FEMALE = 2; //2女 0未知}// 定义一个用户音讯message UserInfo{ string name = 1; // 姓名字段 SexType sex = 2; // 性别字段,应用SexType枚举类型}
运行成果如下:
在理论开发中,咱们须要定义很多的proto,咱们如何做到音讯的复用呢?
答案就是:“音讯嵌套”
1.8 音讯嵌套
咱们在开发Java和PHP时,常常嵌套应用类,也能够应用其余类作为本人的成员属性类型;在开发Go时常常嵌套应用构造体。
在ProtoBuf中同样反对音讯嵌套,能够在一个音讯中嵌套另外一个音讯,字段类型能够是另外一个音讯类型。
咱们来看上面3个经典示例:
1.8.1 援用其余音讯类型的用法
// 定义Article音讯message Article { string url = 1; string title = 2; repeated string tags = 3; // 字符串数组类型}// 定义ListArticle音讯message ListArticle { // 援用下面定义的Article音讯类型,作为results字段的类型 repeated Article articles = 1; // repeated关键词标记,阐明articles字段是一个数组}
1.8.2 音讯嵌套
相似类嵌套一样,音讯也能够嵌套,比方这样:
message ListArticle { // 嵌套音讯定义 message Article { string url = 1; string title = 2; repeated string tags = 3; } // 援用嵌套的音讯定义 repeated Article articles = 1;}
1.8.3 import导入其余proto文件定义的音讯
咱们在理论开发中,通常要定义很多音讯,如果都写在一个proto文件,是不不便保护的。
小技巧:将音讯定义写在不同的proto文件中,在须要的时候能够通过import导入其余proto文件定义的音讯。
例子:
创立文件: article.proto
syntax = "proto3";package nesting;option go_package = "./;article";message Article { string url = 1; string title = 2; repeated string tags = 3; // 字符串数组类型}
创立文件: list_article.proto
syntax = "proto3";// 导入Article音讯定义import "article.proto";package nesting;option go_package = "./;article";// 定义ListArticle音讯message ListArticle { // 应用导入的Result音讯 repeated Article articles = 1;}
执行成果如下,咱们顺利生成了.pb.go文件:
1.9 map类型
咱们在Go语言开发中,最罕用的就是切片类型和map类型了。
切片类型在ProtoBuf中对应的就是repeated类型,后面咱们曾经介绍过了。
再重点介绍一下map类型,ProtoBuf也是反对map类型的:
1.9.1 map语法
map<key_type, value_type> map_field = N;
语法非常简单和通用,然而有几个问题须要咱们留神:
key_type
能够是任何整数或字符串类型(除浮点类型和字节之外的任何标量类型)。- 留神:枚举不是无效的
key_type
。 value_type
能够是除另一个映射之外的任何类型。- Map 字段不能应用
repeated
关键字润饰。
1.9.2 map的例子
咱们举个典型的例子:学生的学科和分数就适宜用map定义:
syntax = "proto3";package map;option go_package = "./;score";message Student{ int64 id = 1; //id string name = 2; //学生姓名 map<string, int32> score = 3; //学科 分数的map}
运行成果如下:
再强调一下
留神:Map 字段是不能应用repeated
关键字润饰。
至此咱们曾经把握了ProtoBuf的所有知识点,是不是非常简单清晰呢?
上面咱们在Go我的项目中实战利用一下ProtoBuf,从ProtoBuf中读取数据,并且转换为咱们罕用的构造体
5分钟实战
1. 首先咱们定义proto文件
我创立了一个demo目录,创立了名为study_info.proto
的文件
syntax = "proto3";package demo;option go_package = "./;study";message StudyInfo { int64 id = 1; //id string name = 2; //学习的科目名称 int32 duration = 3; //学习的时长 单位秒 map<string, int32> score = 4; //学习的分数}
2. 生成代码
应用命令生成pb.go文件:
protoc --go_out=. study_info.proto
3.编写go文件
编写go文件,读取ProtoBuf中定义的字段,进行赋值,取值,转成构造体等操作:
proto编码和解码的操作和json是十分像的,都应用“Marshal”和“Unmarshal”关键字。
package mainimport ( "fmt" "google.golang.org/ProtoBuf/proto" study "juejin/ProtoBuf/proto/demo")func main() { // 初始化proto中的音讯 studyInfo := &study.StudyInfo{} //惯例赋值 studyInfo.Id = 1 studyInfo.Name = "学习ProtoBuf" studyInfo.Duration = 180 //在go中申明实例化map赋值给ProtoBuf音讯中定义的map score := make(map[string]int32) score["实战"] = 100 studyInfo.Score = score //用字符串的形式:打印ProtoBuf音讯 fmt.Printf("字符串输入后果:%v\n", studyInfo.String()) //转成二进制文件 marshal, err := proto.Marshal(studyInfo) if err != nil { return } fmt.Printf("Marshal转成二进制后果:%v\n", marshal) //将二进制文件转成构造体 newStudyInfo := study.StudyInfo{} err = proto.Unmarshal(marshal, &newStudyInfo) if err != nil { return } fmt.Printf("二进制转成构造体的后果:%v\n", &newStudyInfo)}
运行后果如下:
本文总结
ProtoBuf作为开发微服务必选的数据交换协定,基于二进制传输,比json/xml更小,速度更快,应用也十分的简略。
通过这篇文章,咱们不仅学会了ProtoBuf的入门操作,还应用Go语言基于ProtoBuf编码解码了数据,进行了实战。
进阶局部带大家理解了ProtoBuf如何定义音讯、ProtoBuf和Go数据类型的映射、枚举类型如何应用、通过音讯嵌套复用代码、应用map类型时须要留神的问题和小技巧。
微服务架构成为企业我的项目的必然选择已是趋势,如果你只会开发单体我的项目,请关注我,带你一起玩转微服务。
对于更文
近期会更新一系列Go实战进阶的文章,欢送大家关注我,这是近期会更新文章的常识脉络图,感兴趣的小伙伴能够关注一波,欢送日常催更。
天下难事,必作于易。想都是问题,做才有答案。站着不动,永远是观众。
小伙伴们还想看哪些内容,欢送在评论区留言。
一起学习,降级打怪
公众号:程序员降级打怪之旅
微信号:wangzhongyang1993
B站视频:王中阳Go
本文参加了思否技术征文,欢送正在浏览的你也退出。