针对golang 使用proto,有两个功能增强可选包goprotobuf(go官方出品)和gogoprotobuf地址如下
代码语言:javascript复制go get -u code.google.com/p/goprotobuf
//或者 github.com/go-mirrors/goprotobuf
go get -u github.com/gogo/protobuf
gogoprotobuf是完全兼容google protobuf,它生成的代码质量要比goprotobuf高一些。它的扩展功能可以参考文档
https://github.com/gogo/protobuf/blob/master/extensions.md
整体上可以分为6类增强:
1,加快序列化和反序列化速度:Fast Marshalling and Unmarshalling
比如
代码语言:javascript复制gogoproto.marshaler
gogoproto.sizer
gogoproto.marshaler_all
gogoproto.sizer_all
sizer选项true,gogo会相应的message生成
代码语言:javascript复制func Size() int
marshaler为true,gogo为相应的生成:
代码语言:javascript复制func Marshal()([] byte, int)
这个 method会调用Size(),所以marshaler为true的时候,sizer也必须为true。
2,更多可选的go结构:More Canonical Go Structures
代码语言:javascript复制gogoproto.nullable
nullable这个option违背protobuf的初衷。使用它,message序列化后,gogo为message的每个field设置一个值,而google protobuf则是要求如果一个option的field没有被赋值,则序列化的时候不会把这个成员序列化进最终结果的。
代码语言:javascript复制gogoproto.customname
field的名称与message的method的名称一样。
还有
代码语言:javascript复制gogoproto.customtype
自定义类型
3,对于一些不兼容protobuf的功能做成可选项:Goprotobuf Compatibility
代码语言:javascript复制gogoproto.goproto_enum_prefix
如果选项为false,则生成的代码中不加"E_"。
4,生成一些常见的方法,减少我们的代码编写量:Less Typing
代码语言:javascript复制gogoproto.gostring
这个选项为message级别,为true的时候,gogo会为相应的message生成GoString()方法。如果想为所有的message生成这类函数,可以设置package级别的gogoproto.stringer_all为true。
5,生成测试代码和benchmark 代码
代码语言:javascript复制gogoproto.testgen
gogoproto.testgen_all
gogoproto.benchgen
testgen选项为true,则gogo会为相应的message生成一个测试用例与性能测试用例。testgen_all则为相应的package level的option。
6,更多序列化格式:More Serialization Formats
代码语言:javascript复制gogoproto.jsontag
gogoproto.moretag
上面这些字段上的选项很多都可以是文件维度上的选项,具体可以参考文档。
下面我们定义一个proto文件来实战分析下
代码语言:javascript复制syntax = "proto3";
package test;
import "github.com/gogo/protobuf/gogoproto/gogo.proto";
option go_package = "learn/grpc/gogoproto";
//定义服务
service TestService {
rpc SayHello(Request) returns (Response){
}
}
//定义参数类型
message Request {
string message=1;
string Field2 = 2 [(gogoproto.jsontag) = "MyField2", (gogoproto.moretags) = "xml:",comment""];
oneof filed {
int64 uid = 3 [(gogoproto.moretags) = 'form:"uid" validate:"required"'];
}
int32 bar = 4 [(gogoproto.moretags) = 'form:"more_bar"', (gogoproto.jsontag) = 'custom_tag'];
string msg = 5 [(gogoproto.nullable) = true, (gogoproto.customname) = "MyMsg"];
repeated bytes G = 6 [(gogoproto.customtype) = "github.com/gogo/protobuf/test/custom.Uint128", (gogoproto.nullable) = false];
}
message Response {
string message=1;
}
可以看到和普通的proto文件区别是我们引入
代码语言:javascript复制import "github.com/gogo/protobuf/gogoproto/gogo.proto";
这里面声明了扩展的定义,在每一个字段后面定义了
代码语言:javascript复制[(gogoproto.nullable) = true, (gogoproto.customname) = "MyMsg"];
格式的扩展,基本格式为[(key)=val,...]
接着我们生成下代码,首先只生成pb文件和grpc文件
代码语言:javascript复制protoc --go-grpc_out=./hello --proto_path=../../.. --proto_path=. --go_out=./hello --go_opt=paths=source_relative --go-grpc_opt=paths=source_relative hello.proto
对比发现,生成的文件和扩展前的文件一样,说明是兼容proto的。
代码语言:javascript复制type Request struct {
state protoimpl.MessageState
sizeCache protoimpl.SizeCache
unknownFields protoimpl.UnknownFields
Message string `protobuf:"bytes,1,opt,name=message,proto3" json:"message,omitempty"`
Field2 string `protobuf:"bytes,2,opt,name=Field2,proto3" json:"Field2,omitempty"`
// Types that are assignable to Filed:
// *Request_Uid
Filed isRequest_Filed `protobuf_oneof:"filed"`
Bar int32 `protobuf:"varint,4,opt,name=bar,proto3" json:"bar,omitempty"`
Msg string `protobuf:"bytes,5,opt,name=msg,proto3" json:"msg,omitempty"`
G [][]byte `protobuf:"bytes,6,rep,name=G,proto3" json:"G,omitempty"`
}
要生gogo成扩展,需要安装扩展插件
代码语言:javascript复制go get -u github.com/gogo/protobuf/protoc-gen-gogo
然后生成下代码
代码语言:javascript复制protoc --go-grpc_out=./hello --proto_path=../../.. --proto_path=. --go_out=./hello --go_opt=paths=source_relative --go-grpc_opt=paths=source_relative --gogo_out=./hello hello.proto
代码语言:javascript复制type Request struct {
Message string `protobuf:"bytes,1,opt,name=message,proto3" json:"message,omitempty"`
Field2 string `protobuf:"bytes,2,opt,name=Field2,proto3" json:"MyField2" xml:",comment"`
// Types that are valid to be assigned to Filed:
// *Request_Uid
Filed isRequest_Filed `protobuf_oneof:"filed"`
Bar int32 `protobuf:"varint,4,opt,name=bar,proto3" json:"custom_tag" form:"more_bar"`
MyMsg string `protobuf:"bytes,5,opt,name=msg,proto3" json:"msg,omitempty"`
G []github_com_gogo_protobuf_test_custom.Uint128 `protobuf:"bytes,6,rep,name=G,proto3,customtype=github.com/gogo/protobuf/test/custom.Uint128" json:"G"`
XXX_NoUnkeyedLiteral struct{} `json:"-"`
XXX_unrecognized []byte `json:"-"`
XXX_sizecache int32 `json:"-"`
}
对比下,我们发现生成的结构体发生了很多变化
代码语言:javascript复制 Field2 string `protobuf:"bytes,2,opt,name=Field2,proto3" json:"MyField2" xml:",comment"`
Bar int32 `protobuf:"varint,4,opt,name=bar,proto3" json:"custom_tag" form:"more_bar"`
field2多了xml格式的tag,bar多了form格式的tag,并且form的名字改成了more_bar.
代码语言:javascript复制MyMsg string `protobuf:"bytes,5,opt,name=msg,proto3" json:"msg,omitempty"`
生成的golang结构体的字段名字由以前的Msg变成了我们指定的Msg
代码语言:javascript复制 G []github_com_gogo_protobuf_test_custom.Uint128 `protobuf:"bytes,6,rep,name=G,proto3,customtype=github.com/gogo/protobuf/test/custom.Uint128" json:"G"`
G的类型由[][]byte变成了我们定义的类型
代码语言:javascript复制[]github_com_gogo_protobuf_test_custom.Uint128
可以进去看下
github.com/gogo/protobuf@v1.3.2/test/custom/custom.go
代码语言:javascript复制type Uint128 [2]uint64
有没有发现,如果我们的proto定义得足够好,可以少写好多代码。