Protocol Buffers 是谷歌推出的编码标准,它在传输效率和编解码性能上都要优于 JSON。但其代价则是需要依赖中间描述语言(IDL)来定义数据(和服务)的结构(通过 *.proto 文件),并且需要一整套的工具链(protoc 及其插件)来生成对应的序列化和反序列化代码。

除了谷歌官方提供的工具和插件(比如生成 go 代码的 protoc-gen-go)外,我们还可以开发或定制自己的插件,根据业务需要按照 proto 文件的定义生成代码或者文档。

1.1开发流程
开发proto plugin可以分为三步:

从 stdin 读取proto文件(ex: test.proto)
根据proto文件自定义规则构造目标文件结构 (ex: main.go)
将生成的文件通过 stdout 输出(ex: test.pb.go)

example
main.go

func main() {
m := myProtoPlugin{}
protogen.Options{}.Run(m.Generate)
}

type myProtoPlugin struct {
}

//实现Generate方法
func (m myProtoPlugin) Generate(plugin *protogen.Plugin) error {
if len(plugin.Files) < 1 {
return nil
}
f := plugin.Files[len(plugin.Files)-1]
//指定输出文件名,可以是任何自定义的文件格式
fileName := f.GeneratedFilenamePrefix + “.pb.go”
//构造目标文件
generatedFile := plugin.NewGeneratedFile(fileName, f.GoImportPath)
//向目标文件里填充内容
generatedFile.P(“// this file is generated by proto file.”)
generatedFile.P(“package main;”)
_ = generateStructFromMessage(generatedFile, f) return nil
}

func generateStructFromMessage(genFile *protogen.GeneratedFile, protoFile *protogen.File) error {
messages := protoFile.Messages
for _, message := range messages {
print(genFile.QualifiedGoIdent(message.GoIdent))
genFile.P(“type “, message.GoIdent.GoName, " struct{”)
for _, field := range message.Fields {
genFile.P(” “, field.GoName, " “, field.Desc.Kind(), “;”)
}
genFile.P(”}”)
_ = generateGetterMethod(genFile, message)
}
return nil
}

func generateGetterMethod(genFile *protogen.GeneratedFile, message *protogen.Message) error {
fields := message.Fields
for _, field := range fields {
genFile.P(“func (m *”, message.GoIdent.GoName, “) Get”, field.GoName, “() “, field.Desc.Kind(), “{”)
genFile.P(” return m.”, field.GoName)
genFile.P(“}”)
}
return nil
}
test.proto

syntax = “proto2”;
import “google/protobuf/descriptor.proto”;
import “validate.proto”;
package main;
option go_package = “./”;

message test {
optional string a = 1;
}

message People {
optional string name = 1;
optional string phone = 2;
}
test.pb.go

// this file is generated by proto file.
package main

type Test struct {
A string
}

func (m *Test) GetA() string {
return m.A
}

type People struct {
Name string
Phone string
}

func (m *People) GetName() string {
return m.Name
}
func (m *People) GetPhone() string {
return m.Phone
}
命令:

go build main.go && protoc --plugin=protoc-gen-myProtoPlugin=main --myProtoPlugin_out=./ test.proto
执行流程

func run(opts Options, f func(*Plugin) error) error {
if len(os.Args) > 1 {
return fmt.Errorf(“unknown argument %q (this program should be run by protoc, not directly)”, os.Args[1])
}
// 读入proto文件
in, err := ioutil.ReadAll(os.Stdin)
if err != nil {
return err
}
req := &pluginpb.CodeGeneratorRequest{}
// 序列化为GodeGeneratorRequest
if err := proto.Unmarshal(in, req); err != nil {
return err
}
// 将GodeGeneratorRequest转化为Plugin
gen, err := opts.New(req)
if err != nil {
return err
}
// 执行自定义的方法,构造输出文件格式以及内容
if err := f(gen); err != nil {
gen.Error(err)
}
resp := gen.Response()
// 序列化目标文件
out, err := proto.Marshal(resp)
if err != nil {
return err
}
// 输出目标文件
if _, err := os.Stdout.Write(out); err != nil {
return err
}
return nil
}

type CodeGeneratorRequest struct {
state protoimpl.MessageState
sizeCache protoimpl.SizeCache
unknownFields protoimpl.UnknownFields
FileToGenerate []string protobuf:"bytes,1,rep,name=file_to_generate,json=fileToGenerate" json:"file_to_generate,omitempty"
Parameter *string protobuf:"bytes,2,opt,name=parameter" json:"parameter,omitempty"
ProtoFile []*descriptorpb.FileDescriptorProto protobuf:"bytes,15,rep,name=proto_file,json=protoFile" json:"proto_file,omitempty"
CompilerVersion *Version protobuf:"bytes,3,opt,name=compiler_version,json=compilerVersion" json:"compiler_version,omitempty"
}

type CodeGeneratorResponse struct {
state protoimpl.MessageState
sizeCache protoimpl.SizeCache
unknownFields protoimpl.UnknownFields
Error *string protobuf:"bytes,1,opt,name=error" json:"error,omitempty"
SupportedFeatures *uint64 protobuf:"varint,2,opt,name=supported_features,json=supportedFeatures" json:"supported_features,omitempty"
File []*CodeGeneratorResponse_File protobuf:"bytes,15,rep,name=file" json:"file,omitempty"
}
生成的主要对象

Options

除了通过定义一般的message生成目标文件,还可以通过为field或者message甚至file定义扩展属性extension来实现更加灵活、丰富的操作以满足复杂的需求。

例如,可以通过为message的filed加上options来来实现field validator的功能,如下:

通过为phone添加stringValidate option来实现限制phone的长度
message StringFieldRules {
optional int32 min_len = 1;
optional int32 max_len = 3;
}

message People {
optional string name = 1;
optional string phone = 2 [(validate.stringValidated)= {
min_len: 10, max_len: 20;
}];
}
以用field的option构造field validator插件为例,开发步骤可以分为以下几步:

构造validate.proto文件用以描述validator的规则,同时生成valiate.pb.go文件
在目标proto文件里引入validate.proto文件,在想要限定的field上加上自定义的规则,如字符长度,格式等
按照1.1的流程对field的option进行解析,获取option相关属性,如定义的min_len, max_len,然后按照自定义规则进行逻辑编码,生成validator文件
validate.proto

message StringFieldRules {
optional int32 min_len = 1;
optional int32 max_len = 3;
}

extend google.protobuf.FieldOptions {
optional StringFieldRules stringValidated = 5000;
}
validator的generate

func generateValidated(genFile *protogen.GeneratedFile, protoFile *protogen.File) error {
messages := protoFile.Messages
for _, message := range messages {
for _, field := range message.Fields {
// 获取field的options
fieldOptions := field.Desc.Options().(*descriptorpb.FieldOptions)
if fieldOptions == nil || fieldOptions.ProtoReflect() == nil {
continue
}
genFile.P(“func (m *”, message.GoIdent.GoName, “) Validated”, field.GoName, “() bool”, “{”)
// 处理string类型的valdator
if field.Desc.Kind() == protoreflect.StringKind {
ext := proto.GetExtension(fieldOptions, validate.E_StringValidated).(*validate.StringFieldRules)
if ext == nil {
continue
}
min := *ext.MinLen
max := *ext.MaxLen
genFile.P(“if len(m.”, field.GoName, “)< “, min, “|| len(m.”, field.GoName, “)>”, max, “{”)
genFile.P(” return false”)
genFile.P(“}”)
genFile.P(“return true”)
genFile.P(“}”)
}
}

}
return nil

}
生成的validator

func (m *People) ValidatedPhone() bool {
if len(m.Phone) < 10 || len(m.Phone) > 20 {
return false
}
return true
}

proto plugin相关推荐

  1. 使用CSharp编写Google Protobuf插件

    什么是 Google Protocol Buffer? Google Protocol Buffer( 简称 Protobuf) 是 Google 公司内部的混合语言数据标准,目前已经正在使用的有超过 ...

  2. gRPC官方快速上手学习笔记(c#版)

    上手前准备工作 支持操作系统:windows.OS X.Linux.实例采用.net..net core sdk. The .NET Core SDK command line tools. The ...

  3. 基于DotNet Core的RPC框架(一) DotBPE.RPC快速开始

    0x00 简介 DotBPE.RPC是一款基于dotnet core编写的RPC框架,而它的爸爸DotBPE,目标是实现一个开箱即用的微服务框架,但是它还差点意思,还仅仅在构思和尝试的阶段.但不管怎么 ...

  4. Unity中使用gRPC

    时间20180508,使用的unity版本2017.2,unity中的.net 4.6平台还是不稳定版本. 示例代码地址:https://github.com/hiramtan/HigRPC_unit ...

  5. netcore 实现一个简单的Grpc 服务端和客户端

    参考资料,和详细背景不做赘述. 首先定义prop 文件 syntax ="proto3"; package RouteGrpc; service HelloWorld{ rpc S ...

  6. tars C++ docker 环境配置

    挺多小公司使用Tars作为rpc框架的.感觉主要原因有以下几点:1. Tars支持多语言,2. Tars的管理端,可以方便微服务管理和发布.3. 有现成的监控.不过说实在的,Tars 开源版本被阉割了 ...

  7. Drill storage plugin实现原理分析

    Drill Storage Plugin介绍 Drill是一个交互式SQL查询引擎,官方默认支持的数据源有hive.hbase.kafka.kudu.mongo.opentsdb.jdbc等,其中jd ...

  8. protoc gen php,protoc-gen-php --php_out: protoc-gen-php: Plugin output is unparseable.

    背景 业务需要用protobuffer 进行通讯. client: php server: c++ 在github 上找到 Protobuf-PHP (https://github.com/drslu ...

  9. proto的介绍和基础使用

    内容摘抄自书籍<Netty redis zookeeper高并发实战> Protobuf使用 proto文件来预先定义的消息格式.数据包是按照proto文件所定义的消息格式完成二进制码流的 ...

最新文章

  1. iOS_25彩票_幸运转盘
  2. 第一次作业:阅读优秀博文谈感想
  3. 风变编程python第一关脸黑怪我喽_风变编程:Python适合编程初学者学习吗?
  4. 如何获取当前C#程序所有线程的调用栈信息 ?
  5. java控制关键字continue,break,return
  6. 基于tkinter的简易加减乘除计算器
  7. 空间中点到直线的距离
  8. abaqus实例手册_《ABAQUS 6.14超级学习手册》——1.5 ABAQUS帮助文档
  9. java poi 设置标题_java POI操作word2010简单实现多级标题结构
  10. 再论由内而外造就自己
  11. Windows10家庭版更改C盘用户user文件夹名称(小新pro13亲测有效)
  12. Pro Tools 贴士- 使用Snapper快速试听和导入音频素材
  13. 溢米辅导完成C轮1500万美元融资,将用于教研、产品以及技术三大领域
  14. 软件工程师的试炼之地:53道Python面试问答
  15. 摄像头P2P软件提供,完美解决打洞及音视频、用户码传输问题。
  16. php preg_PHP Preg简介
  17. 内容即广告是最好的移动商业模式?
  18. Linux学习笔记——文件大小和文件压缩命令
  19. 基于Detectron2的BlendMask训练 BlendMask环境配置 COCO数据集
  20. Java基础读书笔记

热门文章

  1. 电视root工具_TapTap | 无需Root,成功移植 IOS14,拿下!!!
  2. android应用自启分析与S4启动列表
  3. Android面试Hash原理详解二
  4. Java实现阿里云云通短信通知发送
  5. 怎么将不可编辑的英文PDF文档翻译成中文
  6. Xamarin.Android之绑定库教程
  7. 计算机操作系统——(第四章) 存储器管理/内存管理
  8. RGB与YCbCr颜色空间的转换
  9. 从图森未来到通用、谷歌,自动驾驶怎么样了?
  10. 爬虫学习笔记 -- 实战某电影网(lxml库版)