protobuf和json/xml的区别
Protocol Buffer 和 XML、JSON一样都是结构数据序列化的工具,但它们的数据格式有比较大的区别:
首先,Protocol Buffer 序列化之后得到的数据不是可读的字符串,而是二进制流
其次,XML 和 JSON 格式的数据信息都包含在了序列化之后的数据中,不需要任何其它信息就能还原序列化之后的数据;但使用 Protocol Buffer 需要事先定义数据的格式(.proto 协议文件),还原一个序列化之后的数据需要使用到这个定义好的数据格式
最后,在传输数据量较大的需求场景下,Protocol Buffer 比 XML、JSON 更小(3到10倍)、更快(20到100倍)、使用 & 维护更简单;而且 Protocol Buffer 可以跨平台、跨语音使用。
protobuf使用过程
1.通过 Protocol Buffer 语法描述需要存储的数据结构,就是我们编写的.proto文件
2.过 Protocol Buffer 编译器编译 .proto 文件。将 .proto 文件 转换成对应平台(python、C++、Java)的代码文件
在终端输入下列命令进行编译
protoc -I=$SRC_DIR --xxx_out=$DST_DIR $SRC_DIR/addressbook.proto
# 参数说明
# 1. $SRC_DIR:指定需要编译的.proto文件目录 (如没有提供则使用当前目录)
# 2. --xxx_out:xxx根据需要生成代码的类型进行设置
"""
对于 Java ,xxx = java ,即 -- java_out
对于 C++ ,xxx = cpp ,即 --cpp_out
"""
# 3. $DST_DIR :编译后代码生成的目录 (通常设置与$SRC_DIR相同)
# 4. 最后的路径参数:需要编译的.proto 文件的具体路径
# 编译通过后,Protoco Buffer会根据不同平台生成对应的代码文件
go使用protobuf
1.下载protobuf的编译器protoc,github.com/protocolbuffers/protobu...,根据不同系统安装对应版本,然后把bin目录写到环境变量中,执行protoc –version成功说明安装成功
>libprotoc 3.17.1
2.获取protobuf的编译器插件protoc-gen-go,其他语言使用其他插件
这个仓库官方准备废弃,使用新
go get -u github.com/golang/protobuf/protoc-gen-go
3.编写proto文件,例如hello.proto
syntax = "proto3";
package proto;
option go_package ="./proto";
service Greeter {
rpc Hello(HelloRequest) returns (HelloResponse) {}
}
message HelloRequest {
string name = 1;
}
message HelloResponse {
string greeting = 1;
}
4.编译.proto文件,发现生成greeter.pb.go文件
protoc --go_out=. proto/greeter.proto
protoc编译器原理
protoc编译器是通过插件机制实现对不同语言的支持,比如protoc命令出现--xxx_out
格式的参数,那么protoc将首先查询是否有内置的xxx插件,如果没有内置的xxx插件那么将继续查询当前系统中是否存在protoc-gen-xxx命名的可执行程序,最终通过查询到的插件生成代码。所以我们使用–go_out,表示使用protoc-gen-go可执行程序。
如果我们新写了一个protoc-gen-go-my命令,并且注册插件netrpc, 则可以使用命令 –go-my_out=plugins=netrpc。
protoc-gen-go
而这个可执行文件里面又实现了一层静态插件系统,比如protoc-gen-go内置了一个gRPC插件,用户可以通过--go_out=plugins=grpc
参数来生成gRPC相关代码,否则只会针对message生成相关代码。
自定义插件
我们可以模仿protoc-gen-go,自定义一个插件。
先看看protoc-gen-go源码的main函数(旧版本),所以只要往generator注册自定义的插件对象即可,不用修改这个main函数
package main
import (
"io/ioutil"
"os"
"github.com/golang/protobuf/proto"
"github.com/golang/protobuf/protoc-gen-go/generator" //我们新写的plugin会注册到这里
)
func main() {
g := generator.New()
data, err := ioutil.ReadAll(os.Stdin)
if err != nil {
g.Error(err, "reading input")
}
if err := proto.Unmarshal(data, g.Request); err != nil {
g.Error(err, "parsing input proto")
}
...
}
自定义plugin对象,plugins/netrpc.go
generator会处理proto文件,生成generator.FileDescriptor对象保存里面的信息。
package plugins
import (
"github.com/golang/protobuf/protoc-gen-go/generator"
"google.golang.org/protobuf/types/descriptorpb"
)
//自定义protoc-gen-go插件,通过--go_out=plugins=netrpc 来生成go代码
type NetrpcPlugin struct {
*generator.Generator
}
func init() {
//注册到generator
generator.RegisterPlugin(new(NetrpcPlugin))
}
func (p *NetrpcPlugin) Name() string {
return "netrpc"
}
func (p *NetrpcPlugin) Init(g *generator.Generator) {
p.Generator = g
}
func (p *NetrpcPlugin) GenerateImports(file *generator.FileDescriptor) {
if len(file.Service) > 0 {
p.genImportCode(file)
}
}
func (p *NetrpcPlugin) Generate(file *generator.FileDescriptor) {
for _, svc := range file.Service { //处理proto文件定义的service
p.genServiceCode(svc)
}
}
func (p *NetrpcPlugin) genImportCode(file *generator.FileDescriptor) {
p.P("// TODO: import code")
}
func (p *NetrpcPlugin) genServiceCode(svc *descriptorpb.ServiceDescriptorProto) {
p.P("// TODO: service code, Name = " + svc.GetName())
}
编译自定义插件生成可执行文件protoc-gen-go-netrpc
go build -o protoc-gen-go-netrpc
cp protoc-gen-go-netrpc /home/csx/go/bin #把生成的可执行文件复制到gopath/bin目录
使用自定义的可执行文件来处理hello.proto
protoc --go-netrpc_out=plugins=netrpc:. hello.proto
使用go-micro插件编译protobuf
该命令会在bin目录下生成protoc-gen-micro(.exe),protoc编译器利用protoc-gen-micro插件将.proto文件转换为micro代码风格文件
go get github.com/micro/protoc-gen-micro/v2
2.编译.proto文件,会生成greeter.pb.go, greeter.pb.mico.go
protoc --proto_path=. --micro_out=. --go_out=. proto/greeter.proto
grpc-gateway框架编译protobuf
两个命令会在$GOPATH/bin目录下生成protoc-gen-go,protoc-gen-grpc-gateway
$GOPATH默认是cd ~/go
go get -u github.com/grpc-ecosystem/grpc-gateway/protoc-gen-grpc-gateway
protoc-gen-go使用新仓库
github.com/golang/protobuf已经准备作废
使用新的库
google.golang.org/protobuf
安装go插件
The compiler plugin protoc-gen-go
will be installed in $GOBIN
, defaulting to $GOPATH/bin
go install google.golang.org/protobuf/cmd/protoc-gen-go@latest
protoc --go_out=. hello.proto
grpc使用
新protoc-gen-go命令不再支持plugin参数,需要重新下载protoc-gen-go-grpc命令
go install google.golang.org/grpc/cmd/protoc-gen-go-grpc@v1.1
protoc --go_out=. hello.proto #编译message得到变量
protoc --go-grpc_out=. hello.proto #编译其他数据,依赖message变量
需要生成两个文件,hello.pb.go, hello_grpc.pb.go. 合并为一条语句
protoc –go_out=. –go-grpc_out=. hello.proto