为啥在 Protocol Buffers 3 中删除了 required 和 optional

Posted

技术标签:

【中文标题】为啥在 Protocol Buffers 3 中删除了 required 和 optional【英文标题】:Why required and optional is removed in Protocol Buffers 3为什么在 Protocol Buffers 3 中删除了 required 和 optional 【发布时间】:2015-10-26 09:13:39 【问题描述】:

我最近将gRPCproto3 一起使用,我注意到requiredoptional 已在新语法中被删除。

谁能解释一下为什么在 proto3 中删除了必需/可选?这种约束似乎只是使定义稳健所必需的。

语法proto2:

message SearchRequest 
  required string query = 1;
  optional int32 page_number = 2;
  optional int32 result_per_page = 3;

语法proto3:

syntax = "proto3";
message SearchRequest 
  string query = 1;
  int32 page_number = 2;
  int32 result_per_page = 3;

【问题讨论】:

【参考方案1】:

在 protobuf 3.15 中返回了可选字段

【讨论】:

如果一切都是可选的,那么在上述版本中返回“可选”有什么用? @SubinSebastian 见github.com/protocolbuffers/protobuf/blob/master/docs/… @SubinSebastian 可选,您可以明确检查是否设置了字段。假设您有一个字段int32 confidence。当前,当接收到此类类型的消息时,您无法知道confidence = 0 或未设置置信度之间的区别。因为默认值在序列化中被优化掉了。如果您将该字段标记为optional,那么可能会在序列化中设置一些额外的位,并且将生成一个has_confidence() 方法,以便您在接收端可以消除两者的歧义。【参考方案2】:

你可以在这个protobuf Github issue找到解释:

我们在 proto3 中删除了必填字段,因为必填字段通常被认为是有害的并且违反了 protobuf 的兼容性语义。使用 protobuf 的整个想法是,它允许您从协议定义中添加/删除字段,同时仍然与较新/较旧的二进制文件完全向前/向后兼容。必填字段打破了这一点。您永远无法安全地将必填字段添加到 .proto 定义中,也无法安全地删除现有的必填字段,因为这两种操作都会破坏线路兼容性。例如,如果您向 .proto 定义添加必填字段,则使用新定义构建的二进制文件将无法解析使用旧定义序列化的数据,因为旧数据中不存在必填字段。在一个复杂的系统中,.proto 定义在系统的许多不同组件之间广泛共享,添加/删除必填字段很容易导致系统的多个部分崩溃。我们已经多次看到由此引起的生产问题,并且在 Google 内部几乎所有地方都禁止任何人添加/删除必填字段。出于这个原因,我们完全删除了 proto3 中的必填字段。

去掉“required”后,“optional”就多余了,所以我们也去掉了“optional”。

【讨论】:

我不明白;反序列化和反序列化后删除消息有什么区别?它将被旧客户端删除,因为它不包含所需的字段(例如 id)。 我倾向于同意@ShmuelH。必填字段将以一种或另一种方式成为 api 的一部分。好吧,这是通过提供给双方的语法自动支持的,或者隐藏在后端,它仍然存在。不妨让它在 api 定义中可见 我完全同意@ShmuelH。 API以一种或另一种方式需要字段,并且它对客户了解这一点很有用。这让我觉得我们还没有得到正确的版本控制。 对@ShmuelH 的另一票。如果您以向后不兼容的方式更改您的 API(添加必填字段),那么您肯定希望您的解析器检测到这一点吗?版本化您的 API!如果你愿意,你甚至可以完全在 Protobuf 中完成,使用 oneof MessageV1, MessageV2, etc. 它不能证明最初有必填字段。并且添加必填字段是不兼容的更改,通常应该通过协议版本更改(即新的消息类型)来处理。【参考方案3】:

required 的实用性一直是许多争论和激烈战争的核心。双方都存在大型营地。一个阵营喜欢保证价值的存在并愿意忍受它的局限性,但另一个阵营认为required危险或无益,因为它不能安全地添加或删除。

让我解释一下为什么应该谨慎使用required 字段的原因。如果您已经在使用原型,则无法添加必填字段,因为旧应用程序不会提供该字段,并且应用程序通常不能很好地处理故障。您可以确保首先升级所有旧应用程序,但很容易出错,如果您将 protos 存储在 any 数据存储中(即使是短暂的,例如内存缓存)。删除必填字段时也会出现同样的情况。

许多必填字段“显然”是必需的,直到……它们不是。假设您有一个 id 字段用于 Get 方法。这显然是必需的。除了,稍后您可能需要将 id 从 int 更改为 string,或将 int32 更改为 int64。这需要添加一个新的 muchBetterId 字段,而现在您只剩下旧的 id 字段,必须指定,但最终会被完全忽略。

当这两个问题结合在一起时,有益的required 字段的数量变得有限,阵营争论它是否仍然有价值。 required 的反对者不一定反对这个想法,而是它目前的形式。一些人建议开发一个更具表现力的验证库,可以检查 required 以及像 name.length > 10 这样更高级的东西,同时还要确保有更好的故障模型。

总体而言,Proto3 似乎更倾向于简单,而 required 的删除更简单。但也许更有说服力,删除required 对 proto3 来说是有意义的,当它与其他功能结合使用时,例如删除原语的字段存在和删除覆盖的默认值。

我不是 protobuf 开发人员,对这个主题也没有任何权威,但我仍然希望解释是有用的。

【讨论】:

是的。另请参阅对必填字段可能出现严重错误的事情的扩展说明:capnproto.org/… 可选的没有被移除;在 proto3 中一切都是可选的。但是,是的,primitives 的字段可见性 (has_field) 已被删除。如果您需要字段可见性,请使用wrappers.proto,其中包含StringValue 之类的消息。由于它们是消息,因此 has_field 可用。这实际上是许多语言中常见的“拳击”。 相反,似乎在 proto3 中删除了“可选”。每个字段都存在,并用默认值填充。您无法知道原始字段是由用户填写还是默认填写。消息字段,基本上是指针,是可选的,因为它们可以有一个空值。 我觉得 protobuf 是一门专门用来发动火焰战争的语言 似乎大多数人不想对他们的 API 进行版本控制。他们可以更轻松地为“向后兼容性”提供所有可选内容。

以上是关于为啥在 Protocol Buffers 3 中删除了 required 和 optional的主要内容,如果未能解决你的问题,请参考以下文章

Protocol buffers--python 实践 protocol buffers vs json

理解netty对protocol buffers的编码解码

protocol buffers的编码原理

如何在 Protocol Buffers 中构建数据模型

Protocol Buffers教程

Protocol buffers编写风格指南