在《protoc-gen-go 介绍与源代码分析 》中介绍了基于 protoc-gen-go 制作自定义 protoc go 插件
想要更好的编写自定义插件,必然会遇到如何在 proto 定义文件中,做些自定义内容
本文介绍,如何通过 protobuf 自定义选项生成自定义代码
protobuf 自定义选项可以用在 proto 文件全局域、消息、字段、服务、方法等等上面
并能通过官方提供的 API : proto.GetExtension
来获取这些选项;
也可以自己写 protoc go 插件时,根据选项值不同,自动生成不同的代码;
除此以外,自定义选项还有不少高级应用,本文不会对此展开。
本文,主要介绍下面这个实际用法的制作过程:
比如, proto 文件定义:
syntax = "proto3";
import "broadcast.proto";
package proto;
service Say {
rpc Hello(Request) returns (mypack.NoReply) { option (mypack.Broadcast) = true; }
rpc Ping(Request) returns (mypack.NoReply) { }
}
message Request {
string name = 1;
}
option (mypack.Broadcast) = true;
就是一个自定义选项
自定义 proto go 插件,可以根据这些自定义选项,生成一些自定义代码
比如,生成如下代码:
// For example
type SayService interface {
BroadcastHello(ctx context.Context) error
Ping(ctx context.Context) error
}
有 option (mypack.Broadcast) = true;
标记的,让方法名加上前缀 Broadcast
下面就来介绍下,如何定义 option (mypack.Broadcast) 与如何自定义插件让方法名加上前缀 Broadcast
直接举例说明,类似如下 broadcast.proto
定义:
syntax = "proto3";
package mypack;
option go_package = "github.com/fananchong/test_protobuf_options;mypack";
import "protoc-gen-go/descriptor/descriptor.proto";
extend google.protobuf.MethodOptions { bool Broadcast = 50000; }
message NoReply {}
syntax = "proto3";
package mypack;
option go_package = "github.com/fananchong/test_protobuf_options;mypack";
import "protoc-gen-go/descriptor/descriptor.proto";
extend google.protobuf.MethodOptions { bool Broadcast = 50000; }
message NoReply {}
直接举例说明,类似如下脚本:
set GOPROXY=https://goproxy.io
go get github.com/golang/protobuf
go list -m -f "{{.Dir}}" github.com/golang/protobuf > temp
set /P DEP=<temp
echo %DEP%
protoc -I. -I%DEP% --go_out=. broadcast.proto
copy /y github.com\fananchong\test_protobuf_options\broadcast.pb.go .
rd /s /q github.com
set GOPROXY=https://goproxy.io && go get github.com/golang/protobuf
go list -m -f "{{.Dir}}" github.com/golang/protobuf > temp && set /P DEP=
- 获取 github.com/golang/protobuf 库绝对路径
protoc -I. -I%DEP% --go_out=. broadcast.proto
broadcast.pb.go
copy /y github.com\fananchong\test_protobuf_options\broadcast.pb.go . && rd /s /q github.com
https://github.com/fananchong/test_protobuf_options
直接举例说明,类似如下代码:
func (g *test) generateClientSignature(servName string, method *pb.MethodDescriptorProto) string {
origMethName := method.GetName()
methName := generator.CamelCase(origMethName)
isBroadcast := false
if v, err := proto.GetExtension(method.GetOptions(), mypack.E_Broadcast); err == nil {
isBroadcast = *(v.(*bool))
}
if isBroadcast {
return fmt.Sprintf("Broadcast%s(ctx %s.Context) error", methName, contextPkg)
}
return fmt.Sprintf("%s(ctx %s.Context) error", methName, contextPkg)
}
v, err := proto.GetExtension(method.GetOptions(), mypack.E_Broadcast)
完整插件例子,请参考: https://github.com/fananchong/test_protobuf_options/tree/master/protoc-gen-test
如,定义 test.proto 文件
syntax = "proto3";
import "broadcast.proto";
package proto;
service Say {
rpc Hello(Request) returns (mypack.NoReply) { option (mypack.Broadcast) = true; }
rpc Ping(Request) returns (mypack.NoReply) { }
}
message Request {
string name = 1;
}
并通过 proto_gen_test 生成 test.pb.go :
// 无关代码略
// For example
type SayService interface {
BroadcastHello(ctx context.Context) error
Ping(ctx context.Context) error
}
具体例子,请参见: https://github.com/fananchong/test_protobuf_options_example
本文中,都是举例说明(东西很少,跑通流程又要多次试错,故直接上代码)
需要了解的一些背景知识:
请参见下面的【参考资料】
由于 protobuf 官方文档需要才能查阅,因此,参考资源均来自
主要有:
以上