为什么在协议缓冲区3中删除了必需和可选的内容


214

我最近gRPCproto3和一起使用,并且注意到了这一点,required并且optional已在新语法中将其删除。

有人可以解释一下为什么在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;
}

Answers:


389

required在许多辩论和火焰战争的核心是的有效性。双方都有大营地。一个营地喜欢保证存在一个价值,并愿意忍受它的局限性,但是另一个营地感到required危险或无益,因为它不能安全地添加或删除。

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

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

当这两个问题结合在一起时,受益required领域的数量将变得有限,并且各阵营争辩说它是否仍然有价值。反对者required不一定反对这个想法,而是反对它的当前形式。一些人建议开发一个更具表现力的验证库,该库可以required与更高级的东西(如)一起检查name.length > 10,同时还应确保具有更好的故障模型。

总的来说,Proto3似乎更喜欢简单,而required删除则更简单。但是也许更令人信服,required当与其他功能结合使用时,删除proto3是有意义的,例如,删除基元的字段存在和删除覆盖的默认值。

我不是protobuf开发人员,也不是该主题上的权威,但我仍然希望该解释有用。


23
是的 另请参见对某些必填字段可能会发生严重错误的扩展说明:capnproto.org/…–
Kenton Varda

8
可选项未删除;在proto3中,一切都是可选的。但是是的,已删除了图元的字段可见性(has_field)。如果您需要字段可见性,请使用wrappers.proto,其中包含类似的消息StringValue。由于它们是消息,因此has_field可用。这实际上是许多语言中常见的“装箱”。
埃里克·安德森

9
相反,在proto3中似乎删除了“可选”。每个字段都存在,并用默认值填充。您无法知道原始字段是由用户填写还是默认情况下填写。消息字段基本上是指针,是可选的,因为它们可以具有空值。
无业游民

14
我觉得protobuf是专为引发火焰战争而设计的语言
Randy L

5
似乎大多数人都不想版本化他们的API。对于他们来说,为“向后兼容”而使所有内容变得更容易是容易的。
Holoceo '18年

41

您可以在此protobuf Github问题中找到解释:

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

删除“必需”之后,“可选”只是多余的,因此我们也删除了“可选”。


6
我不明白 反序列化后删除消息与反序列化之间删除消息有什么区别?由于它不包含需要的字段(例如,id),它将由较旧的客户端删除。
Shmuel H.

6
我倾向于同意@ShmuelH。必填字段将以某种方式成为api的一部分。嗯,这是通过提供给双方的语法自动支持的,或者隐藏在后端中,它仍然存在。也可以使它在api定义中可见
Cruncher

7
我完全同意@ShmuelH。API必须以一种或另一种方式填写字段,这对于客户了解这一点很有用。这使我认为我们还没有得到正确的版本控制。
patrickbarker

6
@ShmuelH的另一票。如果以向后不兼容的方式更改API(添加必填字段),那么您确定要让解析器检测到它吗?版本化您的API!如果需要,您甚至可以使用Protobuf完全完成此操作oneof { MessageV1, MessageV2, etc. }
Timmmm

1
它不能证明最初具有必填字段是合理的。并且添加必填字段是不兼容的更改,通常应通过协议版本更改(即新消息类型)来处理。
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.