【问题标题】:Custom protobuf options of message of type Any in GoGo 中 Any 类型消息的自定义 protobuf 选项
【发布时间】:2021-01-19 19:13:50
【问题描述】:

我有一个 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 类型,是不是在 Go 代码中添加了查找发送的类型的功能?或者你不能做一个类型断言?这应该会使静态值变得不必要。

标签: go protocol-buffers any


【解决方案1】:

为了获得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

【讨论】:

    【解决方案2】:

    Go 中的 any.ANY 类型包含 TypeUrl 字段,该字段包含已发送消息的类型。然后,您可以使用 UnmarshalAny 来生成正确的 Go 类型。

    链接到complete guide,了解如何使用any.ANI

    【讨论】:

    • 这只是帮了我一半。但我找到了一个解决方案,我将在今天晚些时候将其作为解决方案发布。
    【解决方案3】:

    对博言的回答稍加补充,发现需要通过protoregistry.ExtensionTypeResolver变量来赋值proto.UnmarshalOptions.Resolver,如下:

    var extRes protoregistry.ExtensionTypeResolver = protoregistry.GlobalTypes
    unmarshalOptions := proto.UnmarshalOptions{Resolver: extRes}
    

    【讨论】:

      猜你喜欢
      • 2019-01-20
      • 2020-11-23
      • 1970-01-01
      • 2022-11-11
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2016-07-17
      相关资源
      最近更新 更多