在使用 GRPC 时,常规的操作是将 message 定义好后进行数据传输,但总会遇到某些数据结构进行组合的操作,采用默认的定义 message 方式,造成代码量的激增。为了解决这个问题 protobuf 提供类型 any 解决 GRPC 中泛型的处理方式
目录结构
.
├── main.go
└── rpc
├── rsp.pb.go
└── rsp.proto
首先,我们定义我们需要传输的消息
path
project_dir/rpc/rsp.proto
采用 proto3 协议
定义 TestAny 用于测试 any 的动态传输
定义 Response 作为 GRPC 通用的消息交互
rsp.proto
内容
syntax = "proto3";
package rpc;
option go_package = ".;rpc";
import "google/protobuf/any.proto";
message TestAny {
uint64 Id = 1;
string Title = 2;
string Content = 3;
}
message Response {
uint32 Code = 1;
string Msg = 2;
google.protobuf.Any data = 3;
}
使用 protoc 编译工具,编译 rsp.proto,生成 rsp.pb.go 文件
path
project_dir/rpc/rsp.pb.go
我们并不需要关注 rsp.pb.go 内部的所有内容
仅需要关注生成的消息体
> protoc --go_out=./ rsp.proto
rsp.pb.go
内容
通过 protoc 工具,生成了两个 struct,及为我们定义在 proto 文件中的 message
type TestAny struct {
state protoimpl.MessageState
sizeCache protoimpl.SizeCache
unknownFields protoimpl.UnknownFields
Id uint64 `protobuf:"varint,1,opt,name=Id,proto3" json:"Id,omitempty"`
Title string `protobuf:"bytes,2,opt,name=Title,proto3" json:"Title,omitempty"`
Content string `protobuf:"bytes,3,opt,name=Content,proto3" json:"Content,omitempty"`
}
type Response struct {
state protoimpl.MessageState
sizeCache protoimpl.SizeCache
unknownFields protoimpl.UnknownFields
Code uint32 `protobuf:"varint,1,opt,name=Code,proto3" json:"Code,omitempty"`
Msg string `protobuf:"bytes,2,opt,name=Msg,proto3" json:"Msg,omitempty"`
Data *any.Any `protobuf:"bytes,3,opt,name=data,proto3" json:"data,omitempty"`
}
测试使用 any
下面我们将演示实际场景中的应用
通过ptypes.MarshalAny(marshal)
将我们定义的消息进行编码
通过ptypes.UnmarshalAny(any, unmarshal)
对已经编码的消息进行反编码
main.go
内容
func main() {
marshal := &rpc.TestAny{
Id: 1,
Title: "标题",
Content: "内容",
}
any, err := ptypes.MarshalAny(marshal)
fmt.Println(any, err) // [type.googleapis.com/rpc.TestAny]:{Id:1 Title:"标题" Content:"内容"} <nil>
msg := &rpc.Response{
Code: 0,
Msg: "success",
Data: any,
}
fmt.Println(msg) // Msg:"success" data:{[type.googleapis.com/rpc.TestAny]:{Id:1 Title:"标题" Content:"内容"}}
unmarshal := &rpc.TestAny{}
err = ptypes.UnmarshalAny(msg.Data, unmarshal)
fmt.Println(unmarshal, err) // Id:1 Title:"标题" Content:"内容" <nil>
}