Go 中 Any 类型消息的自定义 protobuf 选项
Custom protobuf options of message of type Any in Go
我有一个 GRPC 服务定义如下:
message SendEventRequest {
string producer = 1;
google.protobuf.Any event = 2;
}
message SendEventResponse {
string event_name = 1;
string status = 2;
}
service EventService {
rpc Send(SendEventRequest) returns (SendEventResponse);
}
我还定义了自定义消息选项:
extend google.protobuf.MessageOptions {
// event_name is the unique name of the event sent by the clients
string event_name = 50000;
}
我想要实现的是让客户创建自定义原型消息,将 event_name
选项设置为“常量”。例如:
message SomeCustomEvent {
option (mypackage.event_name) = "some_custom_event";
string data = 1;
...
}
这样服务就可以跟踪正在发送的事件。当我做这样的事情时,我能够从特定的 proto.Message
:
中获取选项的值
_, md := descriptor.MessageDescriptorProto(SomeCustomEvent)
mOpts := md.GetOptions()
eventName := proto.GetExtension(mOpts, mypackage.E_EventName)
但是,当消息的类型为 github.com/golang/protobuf/ptypes/any.Any
时,选项为零。如何从消息中检索 event_name
?我遇到了 protoregistry.MessageTypeResolver
,这看起来可能有帮助,但我需要想出一种方法来在客户端集成时动态更新事件的原型定义。
any.ANY type in Go contains the TypeUrl
field which contains the type of the message that was sent. You can then use that to UnmarshalAny 到正确的生成的 Go 类型。
Link 到 complete guide 如何使用 any.ANI
。
为了获得 Any
类型的选项,您需要其特定的 protoreflect.MessageType
以便您可以将其解组为特定的消息。为了获取消息类型,您需要 MessageTypeResolver
.
Any
包含一个 type_url
字段,可用于该目的。为了将 Any
对象解组为现有消息类型的消息:
// GlobalTypes contains information about the proto message types
var res protoregistry.MessageTypeResolver = protoregistry.GlobalTypes
typeUrl := anyObject.GetTypeUrl()
msgType, _ := res.FindMessageByURL(typeUrl)
msg := msgType.New().Interface()
unmarshalOptions := proto.UnmarshalOptions{Resolver: res}
unmarshalOptions.Unmarshal(anyObject.GetValue(), msg)
收到特定消息后,您可以简单地获取您需要的选项:
msgOpts := msg.ProtoReflect().Descriptor().Options()
eventName := proto.GetExtension(msgOpts, mypackage.E_EventName)
请注意,如果消息未扩展 event_name
选项,proto.GetExtension
将崩溃,并且需要恢复。这个块可以加在函数的开头:
defer func() {
if r := recover(); r != nil {
// err is a named return parameter of the outer function
err = fmt.Errorf("recovering from panic while extracting event_name from proto message: %s", r)
}
}()
编辑:请注意,应用程序必须导入包含原型定义的包,以便 protoregistry.GlobalTypes
识别类型。你可以在你的代码中做这样的事情:
var _ mypackage.SomeEvent
博彦的回答有点补充,我发现它需要传递protoregistry.ExtensionTypeResolver
变量来分配proto.UnmarshalOptions.Resolver
,看起来像:
var extRes protoregistry.ExtensionTypeResolver = protoregistry.GlobalTypes
unmarshalOptions := proto.UnmarshalOptions{Resolver: extRes}
我有一个 GRPC 服务定义如下:
message SendEventRequest {
string producer = 1;
google.protobuf.Any event = 2;
}
message SendEventResponse {
string event_name = 1;
string status = 2;
}
service EventService {
rpc Send(SendEventRequest) returns (SendEventResponse);
}
我还定义了自定义消息选项:
extend google.protobuf.MessageOptions {
// event_name is the unique name of the event sent by the clients
string event_name = 50000;
}
我想要实现的是让客户创建自定义原型消息,将 event_name
选项设置为“常量”。例如:
message SomeCustomEvent {
option (mypackage.event_name) = "some_custom_event";
string data = 1;
...
}
这样服务就可以跟踪正在发送的事件。当我做这样的事情时,我能够从特定的 proto.Message
:
_, md := descriptor.MessageDescriptorProto(SomeCustomEvent)
mOpts := md.GetOptions()
eventName := proto.GetExtension(mOpts, mypackage.E_EventName)
但是,当消息的类型为 github.com/golang/protobuf/ptypes/any.Any
时,选项为零。如何从消息中检索 event_name
?我遇到了 protoregistry.MessageTypeResolver
,这看起来可能有帮助,但我需要想出一种方法来在客户端集成时动态更新事件的原型定义。
any.ANY type in Go contains the TypeUrl
field which contains the type of the message that was sent. You can then use that to UnmarshalAny 到正确的生成的 Go 类型。
Link 到 complete guide 如何使用 any.ANI
。
为了获得 Any
类型的选项,您需要其特定的 protoreflect.MessageType
以便您可以将其解组为特定的消息。为了获取消息类型,您需要 MessageTypeResolver
.
Any
包含一个 type_url
字段,可用于该目的。为了将 Any
对象解组为现有消息类型的消息:
// GlobalTypes contains information about the proto message types
var res protoregistry.MessageTypeResolver = protoregistry.GlobalTypes
typeUrl := anyObject.GetTypeUrl()
msgType, _ := res.FindMessageByURL(typeUrl)
msg := msgType.New().Interface()
unmarshalOptions := proto.UnmarshalOptions{Resolver: res}
unmarshalOptions.Unmarshal(anyObject.GetValue(), msg)
收到特定消息后,您可以简单地获取您需要的选项:
msgOpts := msg.ProtoReflect().Descriptor().Options()
eventName := proto.GetExtension(msgOpts, mypackage.E_EventName)
请注意,如果消息未扩展 event_name
选项,proto.GetExtension
将崩溃,并且需要恢复。这个块可以加在函数的开头:
defer func() {
if r := recover(); r != nil {
// err is a named return parameter of the outer function
err = fmt.Errorf("recovering from panic while extracting event_name from proto message: %s", r)
}
}()
编辑:请注意,应用程序必须导入包含原型定义的包,以便 protoregistry.GlobalTypes
识别类型。你可以在你的代码中做这样的事情:
var _ mypackage.SomeEvent
博彦的回答有点补充,我发现它需要传递protoregistry.ExtensionTypeResolver
变量来分配proto.UnmarshalOptions.Resolver
,看起来像:
var extRes protoregistry.ExtensionTypeResolver = protoregistry.GlobalTypes
unmarshalOptions := proto.UnmarshalOptions{Resolver: extRes}