Go发布基于反射的新版 Protobuf API

Go 的 protocol buffer 绑定是用于谷歌的语言无关的数据交换格式,旨在取代JSON用于高性能应用程序,目的是 protocol buffer 系统合并到Go的类型系统中,并在运行时实现其操作。

protocol buffer提供了一种方法以指定用于传输结构化数据的模式。这种模式通常被转换成一种特定于编程语言的表示形式,称为绑定,使得使用高级别的接口处理protobuf消息变得更容易。

根据新protobuf模块版本的作者Joe Tsai、Damien Neil和Herbie Ong的说法,之前protobuf实现已经不能满足Go开发人员的期望了。具体来说,它虽然提供了Go 类型和值的视图,但是忽略了 protocol buffer 类型系统中的信息。

这样做的后果就是portobuf注解的丢失。例如,我们可能想编写一个遍历日志条目并清除任何注解(annotation)为包含敏感数据的字段的函数。注解不是 Go 类型系统的一部分。

旧的portobuf模块的另一个局限是依赖于静态绑定,从而阻碍了动态消息的使用,而动态消息的类型在编译时不是完全可知的。

新的protobuf模块 (版本为APIv2)基于这个假设:protobuf Message必须完全指定消息的行为,并使用反射以提供protobuf类型的完整视图。Go protobuf APIv2的基石是新的proto.Message接口,可用于所有生成的消息类型,并提供了访问消息内容的方式。这包括所有protobuf字段,可以使用protoreflect.Message.Range方式对它们进行迭代。该方法既可以处理动态消息,也可以访问消息选项。下面例子说明如何处理消息以在进一步处理前清除其包含的所有敏感信息:

// Redact清除pb中的每个敏感字段。
func Redact(pb proto.Message) {
    m := pb.ProtoReflect()
    m.Range(func(fd protoreflect.FieldDescriptor, v protoreflect.Value) bool {
        opts := fd.Options().(*descriptorpb.FieldOptions)
        if proto.GetExtension(opts, policypb.E_NonSensitive).(bool) {
            return true
        }
        m.Clear(fd)
        return true
    })
}

然而,几个Hacker News评论者指出,Go protobuf APIv2的版本控制有点让人感到困惑 。开发人员需要把新版本绑定到一个特定的存储库,而不能使用新的版本扩展现有的存储库,并把它标记为v2。Damien Neil解释了这一决定背后的原因,如下所示:

我们可以把新的API标记为v2: 在导入路径中,把v1和v2清楚地区分开来。 让人感到困惑的:google.golang.org/protobuf@v1不存在,而v2存在。 10年后,希望没人关心这个旧的github.com/golang/protobuf,那么,这个令人困惑的事就不存在了。 我们可以把新的API标记为v1: 在导入路径中难以清楚地区分开来。 把google.golang.org/protobuf的第一个版本标记为v1是有意义的。 如果我们认为它是个糟糕的想法,那么,从v1转到v2比从v2回退到v1更容易一些。

此外,Go protobuf APIv2将从1.20版开始。Neil对此做了的解释,这样的目的是避免在错误报告中出现版本重叠产生的歧义, 他认为Go protobuf APIv1永远不会有1.20版。

最后要注意的是,APIv1不会被APIv2淘汰,它会得到无限期维护。事实上,其最新的实现(1.4版)是在APIv2之上实现的。

阅读原文:

Go Gets New Reflection-Based Protocol Buffers API

  • 发表于:
  • 本文为 InfoQ 中文站特供稿件
  • 首发地址https://www.infoq.cn/article/dyX9bq5uFyK4hCEPgmuj
  • 如有侵权,请联系 yunjia_community@tencent.com 删除。

扫码关注云+社区

领取腾讯云代金券