【问题标题】:How do I represent a UUID in a protobuf message?如何在 protobuf 消息中表示 UUID?
【发布时间】:2021-11-12 04:55:51
【问题描述】:

我想将 UUID 附加到我的 protobuf 用户消息示例中的字段。

message User {
  // field containing id as UUID type
  required string email;
  optional string name;
}

我知道 protobuf 消息还不支持 UUID 类型。我读过最好的方法是使用 UUID 消息类型。

所以我猜我的用户消息会导入我的 UUID 消息原型定义并将其用作字段类型,如下所示:

import "myproject/UUID.proto";

message User {
  required UUID id;
  required string email;
  optional string name;
}

我的问题是,UUID 消息的外观如何,我将如何对其进行编码/解码?我的目标是 Java/Scala 和 C# 兼容性。

【问题讨论】:

    标签: protocol-buffers uuid


    【解决方案1】:

    您可能应该使用stringbytes 来表示UUID。如果将 UUID 保持为人类可读格式(例如 "de305d54-75b4-431b-adb2-eb6b9e546014")最方便,请使用 string;如果您存储 128 位原始值,请使用 bytes。 (如果你不确定,你可能想要string。)

    将值包装在名为UUID 的消息类型中有助于使代码更具自记录性,但会产生一些性能开销,并非严格要求。如果您想这样做,请定义如下类型:

    message UUID {
      required string value = 1;
    }
    

    或:

    message UUID {
      required bytes value = 1;
    }
    

    【讨论】:

    • @Hyperreal:FWIW,要转换为字节,您将删除连字符,然后将其余部分解释为十六进制字符串——每两个数字变成一个字节。但是,是的,存储字符串可能更容易。
    • 所以我查找了您正在处理的新协议缓冲区(Cap'N Proto),无论如何我可能会完全放弃 Google 的 Protobuff ......我看到有人也在开发 Java 实现(希望将来也可以使用 Scala!)。
    • @Hyperreal 哈,好吧,注意同样的问题也适用于 Cap'n Proto,答案基本相同。 :)
    • 我同意 protobuf 团队在这里的决定。使用 stringbytes 来表示 UUID 很容易。添加内置类型需要在每种语言的 protobuf 实现中增加复杂性。这不值得。
    • @KingKong 似乎答案是扩展 arenas 以支持字符串和字节。
    【解决方案2】:

    如果有的话,您想使用string 来避免字节序问题。请注意,具有相同字符串表示形式(因此是相同的“id”)的 UUID 和 MS GUID 具有不同的字节流顺序(大端与小端)。如果您在协议中使用 bytes 在使用 UUID 的 Java 和使用 System.Guid 的 C# 之间进行通信,则最终可能会出现翻转的 ID。

    【讨论】:

    • 字节顺序问题仅影响多字节值,如 16、32 和 64 整数和浮点数,其中必须选择哪一端包含高阶字节。对于字节数组,没有字节序,因此使用bytes 不会导致任何字节序问题。另一方面,如果 UUID 存储在两个 64 位整数中,则必须处理字节顺序问题。
    • @DougRichardson,GUID/UUID is not merely an array of bytes。它具有 4/2/2/2/6 字节结构,并且字节顺序很重要。
    • @RobertTaylor 当您生成 UUID 以符合 RFC 4122 时确实如此,但是一旦生成它,您就可以将其视为不透明的字节数组。要了解我的意思,请查看the Go UUID library。版本 1 和版本 4 都创建考虑字节顺序的 UUID,但 marshalling 将这两个版本都视为简单的字节数组。由于这个问题是关于 protobufs 的,所以我们只关心编组方面。
    • @RobertTaylor,您提供的链接(格式部分)仅定义了“规范文本表示”。而 UUID 本身实际上只是一个“128 位数字”,没有任何其他特殊限制。我什至会从该定义中删除“数字”一词,并将其定义为“128 位序列”。
    【解决方案3】:

    我没有足够的声望点来发表评论,所以我必须写这个作为答案。

    使用字符串,而不是像其他评论者所说的那样使用字节数组。根据 MS (https://docs.microsoft.com/en-us/dotnet/architecture/grpc-for-wcf-developers/protobuf-data-types) 的说法,“不要将字节字段用于 Guid 值。当 Protobuf 与其他平台(例如 Java)交互时,字节序问题(维基百科定义)可能会导致行为不稳定。”

    【讨论】:

    • 当他们只是“懒惰”或只是“忘记”通过不忘记考虑字节序来正确实现逻辑时,他们可能会出现“不稳定的行为”。这不是 UUID 表示问题,而是仅以“规范文本格式”“呈现”UUID 的逻辑的潜在错误实现。值本身总是正好是 128 位的序列。
    • 我很抱歉,但我终生无法理解您的评论。
    【解决方案4】:

    如果您想确保直接的互操作性,我建议使用字符串编码而不是字节编码:

    message UUID {
      required string value = 1;
    }
    

    字节编码的问题是:不同的 UUID 库对字节使用不同的编码/解码方案,但它们同意如何编码/解码字符串。

    例如,请参阅 C# 的 System.guid.toBytesArray 返回混合端格式:前三个组件是小端编码的,而后两个是大端编码的。

    在 Java 中,Apache Commons Library Uuid.toRawBytes 以大端编码返回 uuid:

    "String": 35918bc9-196d-40ea-9779-889d79b753f0
    "C#"    : C9 8B 91 35 6D 19 EA 40 97 79 88 9D 79 B7 53 F0
    "Java"  : 35 91 8B C9 19 6D 40 EA 97 79 88 9D 79 B7 53 F0
    

    附带说明:Python 3 的 Uuid 提供两种编码:bytes 用于大端编码,bytes_le 用于混合端编码。

    【讨论】:

      猜你喜欢
      • 2017-02-14
      • 1970-01-01
      • 1970-01-01
      • 2020-05-24
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多