解码“one-of”protobuf消息

问题描述 投票:0回答:1

我正在实施 protobuf 扩展,以与我们现有的内部 protobuf 框架配合使用。 我们已经在多个服务中针对 C、Python 和其他几种语言实现了此功能,因此更改消息格式不是一个选择。 我想做的是找到最惯用的(也是最不痛苦的)方法。

我们所做的是创建一个小标头,将其添加到所有 protobuf 消息中,其中一个字段指示消息类型。 在这个框架中编码消息非常简单:

const (
    MessageEmpty = iota
    Message1
    Message2
)

func Encode(msgType byte, msg proto.Message) ([]byte, error) {
    buf, err := proto.Marshal(msg)
    if err != nil {
        return nil, err
    }

    hdr := Header {
        msgtype: msgType,
    }

    out := new(bytes.Buffer)
    err = binary.Write(out, binary.BigEndian, hdr)
    if err != nil {
        return nil, err
    }
    
    out.Grow(len(buf))
    _, err = out.Write(buf)
    if err != nil {
        return nil, err
    }

    return out.Bytes(), nil
}

我还没有彻底测试过这一点,但乍一看似乎是正确的。 但是,我遇到的问题是尝试编写 Decode() 函数。 我想做的是以下几点:

func Decode(pkt []byte) (proto.Message, error) {
    hdr := Header{}
    buf := bytes.NewBuffer(pkt)
    ret := proto.Message()

    err := binary.Read(buf, binary.BigEndian, &hdr)
    if err != nil {
        return nil, err
    }
    
    msg := buf.Bytes()
    switch hdr.msgType {
    case Message1:
        pbmsg := myproto.FirstMessage{}
        err = proto.Unmarshal(msg, &pbmsg)
        ret = &pbmsg
    case Message2:
    }

    if err != nil {
        return nil, err
    }

    return ret, nil
}

但是,这无法编译。 我将遇到的下一个问题是,一旦我收到消息,我将如何在运行时确定该消息的类型? (我想我也可以返回 msgType 字段作为我的返回值之一)。

欢迎任何建议和/或指导。

go protocol-buffers
1个回答
0
投票

您可能不应该推出自己的机制。对于动态消息,可以使用 google/protobuf/any.proto ,如下所示:

import (
    "google.golang.org/protobuf/proto"
    "google.golang.org/protobuf/types/known/anypb"
)

func Encode(msg proto.Message) ([]byte, error) {
    anyMsg, err := anypb.New(msg)
    if err != nil {
        return nil, err
    }

    return proto.Marshal(anyMsg)
}

func Decode(buf []byte) (proto.Message, error) {
    var anyMsg anypb.Any
    if err := proto.Unmarshal(buf, &anyMsg); err != nil {
        return nil, err
    }

    return anyMsg.UnmarshalNew()
}

返回的值可以与

switch
:

一起使用
    msg, err := Decode(buf)
    if err != nil {
        // ...
    }

    switch msg := msg.(type) {
    case *myproto.FirstMessage:
        // Here msg is of type *myproto.FirstMessage
        // ...
    }

这也回答了你的第二个问题。

假设出于兼容性原因,您需要支持您的格式,则将编译以下内容:

func Decode(pkt []byte) (proto.Message, error) {
    buf := bytes.NewBuffer(pkt)

    var hdr Header
    err := binary.Read(buf, binary.BigEndian, &hdr)
    if err != nil {
        return nil, err
    }

    msg := buf.Bytes()

    var ret proto.Message
    switch hdr.MsgType {
    case Message1:
        var pbmsg models.CreateUserRequest
        err = proto.Unmarshal(msg, &pbmsg)
        ret = &pbmsg
    case Message2:
        // ...
    }

    return ret, err
}
© www.soinside.com 2019 - 2024. All rights reserved.