protobuf的使用

Golang
551
0
0
2022-07-13
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...