protobuf的使用

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

protobuf3 语法#

www.cnblogs.com/tohxyblog/p/897476...

本作品采用《CC 协议》,转载必须注明作者和本文链接
走出舒适区