解组未知 Protobuf 消息的陷阱
proto.Unmarshal 无法处理 interface{} 类型是需要理解的一个重要方面。它的方法签名规定传递一个 proto.Message 参数,该参数由具体的 protobuffer 类型实现。
克服挑战
处理缺乏额外上下文的原始 protobuffer 有效负载时,在字节切片旁边至少有一些标识信息(例如字符串或数字)至关重要。该信息可用于映射到特定的 protobuffer 具体消息。然后,您可以使用 switch 语句实例化适当的类型并将其传递给 Unmarshal:
switch atLeastSomething { case "foo": message = &mypb.Foo{} case "bar": message = &mypb.Bar{} } _ = proto.Unmarshal(data, message)
拥抱不可预测:解码完全未知的消息
在极少数情况下,您可能会遇到完全未知的 protobuffer 有效负载。 protowire 包提供了一种从中提取一些信息的解决方案,尽管存在固有的局限性。可以检索有效负载内容,但语义较弱。
实现细节
这是未知原始消息的简化解析器:
func parseUnknown(b []byte) []Field { // Data model type Field struct { Tag Tag Val Val } type Tag struct { Num int32 Type protowire.Type } type Val struct { Payload interface{} Length int } // Parsing algorithm fields := make([]Field, 0) for len(b) > 0 { n, t, fieldlen := protowire.ConsumeField(b) if fieldlen < 1 { return nil } ... // Parsing logic fields = append(fields, field) b = b[fieldlen:] } return fields }
示例输入和输出
使用示例输入:
message Foo { string a = 1; string b = 2; Bar bar = 3; } message Bar { string c = 1; } &test.Foo{A: "A", B: "B", Bar: &test.Bar{C: "C"}}
输出将是:
Field{Tag:Tag{Num:1, Type:2}, Val:Val{Payload:[]uint8{0x41}, Length:1}} Field{Tag:Tag{Num:2, Type:2}, Val:Val{Payload:[]uint8{0x42}, Length:1}} Field{Tag:Tag{Num:1, Type:2}, Val:Val{Payload:[]uint8{0x43}, Length:1}} Field{Tag:Tag{Num:3, Type:2}, Val:Val{Payload:[]Field{Field{Tag:Tag{Num:1, Type:2}, Val:Val{Payload:[]uint8{0x43}, Length:1}}}, Length:3}}
其他注意事项
替代方案:Any 和 Interface{}
虽然 Any 最初看起来像是未知消息的合适选项,但它具有严格定义的结构,并且不能用于解码未知的有效负载。此外,由于其特殊的结构,protowire 不适用于 Any。
以上是如何在 Go 中解组未知的 Protobuf 消息?的详细内容。更多信息请关注PHP中文网其他相关文章!