这是一篇姊妹篇文章,浅析一下Go是如何实现protobuf编解码的:
- Go是如何实现protobuf的编解码的(1): 原理
- Go是如何实现protobuf的编解码的(2): 源码
本编是第一篇。
Protocol Buffers介绍
Protocol buffers缩写为protobuf,是由Google创造的一种用于 序列化 的标记语言,项目 Github 仓库:。
Protobuf主要用于不同的 编程语言 的协作RPC场景下,定义需要序列化的数据格式。Protobuf本质上仅仅是 一种用于交互的结构式定义 ,从功能上 和XML、JSON 等各种其他的交互形式都 并无本质不同,只负责定义不负责数据编解码 。
其官方介绍如下:
Protocol buffers的多语言支持
protobuf是支持多种编程语言的,即多种编程语言的类型数据可以转换成protobuf定义的类型数据,各种语言的类型对应可以看此介绍。
我们介绍一下protobuf对多语言的支持原理。protobuf有个程序叫 protoc ,它是一个编译程序, 负责把proto文件编译成对应语言的文件 ,它已经支持了C++、C#、Java、Python,而对于Go和Dart需要安装插件才能配合生成对于语言的文件。
对于C++,protoc可以把a.proto,编译成a.pb.h和a.pb.cc。
对于Go,protoc需要使用插件 protoc-gen-go ,把a.proto,编译成a.pb.go,其中包含了定义的数据类型,它的序列化和反序列化函数等。
敲黑板,对Go语言,protoc只负责利用protoc-gen-go把proto文件编译成Go语言文件,并不负责序列化和反序列化,生成的Go语言文件中的序列化和反序列化操作都是只是wrapper。
那Go语言对protobuf的序列化和反序列化,是由谁完成的?
由github.com/golang/protobuf/proto完成,它负责把 结构体 等序列化成proto数据([]byte),把proto数据反序列化成Go结构体。
OK,原理部分就铺垫这些,看一个简单样例,了解protoc和protoc-gen-go的使用,以及进行序列化和反序列化操作。
一个Hello World样例
根据上面的介绍,Go语言使用protobuf我们要先安装2个工具:protoc和protoc-gen-go。
安装protoc和protoc-gen-go
首先去下载页下载符合你系统的protoc,本文示例版本如下:
protoc的安装 步骤在readme.txt中:
To install, simply place this bin ary somewhere in your PATH.
把protoc-3.9.0-osx-x86_64/bin加入到PATH。
If you intend to use the include d well known types then don’t forget tocopy the contents of the ‘include’ directory somewhere as well, for exampleinto ‘/usr/local/include/‘.
如果使用已经定义好的类型,即上面include目录*.proto文件中的类型,把include目录下文件,拷贝到/usr/local/include/。
安装protoc-gen-go:
go get –u github.com/golang/protobuf/protoc-gen-go
检查安装,应该能查到这2个程序的位置:
➜ fabric git:(release-1.4) which protoc /usr/local/bin/protoc ➜ fabric git:(release-1.4) which protoc-gen-go /Users/shitaibin/go/bin/protoc-gen-go
Hello world
创建了一个使用protoc的小玩具,项目地址Github: golang_step_by_step。
它的目录结构如下:
➜ protobuf git:(master) tree helloworld1
helloworld1
├── main.go
├── request .proto
└── types
└── request.pb.go
定义proto文件
使用proto3,定义一个Request,request.proto内容如下:
// file: request.proto
syntax = "proto3";
package helloworld;
option go_package="./types";
message Request {
string data = 1;
}
- syntax:protobuf版本,现在是proto3
- package:不完全等价于Go的package,最好另行设定go_package,指定根据protoc文件生成的go语言文件的package名称。
- message:会编译成Go的struct。
- string data = 1:代表request的成员data是string类型,该成员的id是1,protoc给每个成员都定义一个编号,编解码的时候使用编号代替使用成员名称,压缩数据量。
编译proto文件
$ protoc –go_out=. ./request.proto
–go_out指明了要把./request.proto编译成Go语言文件,生成的是./types/request.pb.go,注意观察一下为Request结构体生产的2个方法XXX_Unmarshal和XXX_Marshal,文件内容如下:
编写Go语言程序
下面这段测试程序就是创建了一个请求,序列化又反序列化的过程。
运行结果:
➜ helloworld1 git:(master) go run main.go req: data:"Hello LIB" unmarshaledReq: data:"Hello LIB"
以上都是铺垫,下一节的proto包怎么实现编解码才是重点,protobuf用法可以去翻:
- 官方介绍:protoc3介绍,编码介绍,Go教程
- 煎鱼grpc系列文章
参考文章
- 《序列化和反序列化》出自美团技术团队,值得一读。
- Go支持protocol buffer的仓库,Readme,值得详读。
- Google Protocol Buffers的Go语言tutorial,值得详细阅读和实操。
- Google Protocol Buffers的Overview,介绍了什么是Protocol Buffers,它的原理、历史(起源),以及和XML的对比,必读。
- 《Language Guide (proto3)》这篇文章介绍了proto3的定义,也可以理解为.proto文件的语法,就如同Go语言的语法,不懂语法怎么编写.proto文件?读这篇文章会了解很多原理,以及可以少踩坑,必读。
- 《Go Generated Code》这篇文章详细介绍了protoc是怎么用.protoc生成.pb.go的,可选。
- #
- 《Protocol Buffers Encoding》这篇介绍编码原理,可选。
- 《package proto文档》可以把proto包当做Go语言操作protobuf数据的SDK,它实现了结构体和protobuf数据的转换,它和.pb.go文件配合使用。
原文链接:
本文作者:大彬,原创授权发布