如何知道字节数组是哪个protobuf消息?

Xu *_*Yao 3 message-queue protocol-buffers

我想使用protobuf而不是Json在消息队列之间进行通信。

当只有一条原始消息时,我知道如何处理。

假设原始文件为:

//person.proto
syntax = "proto3";

option java_outer_classname = "PersonProto";

message Person {
    int32 id = 2;
    string name = 1;
    string email = 3;
}
Run Code Online (Sandbox Code Playgroud)

现在,我可以使用以下方法处理它:

PersonProto.Person person = PersonProto.Person.newBuilder()
        .setEmail("123@test.com")
        .setId(1)
        .setName("name-test")
        .build();

byte[] bytes = person.toByteArray();

//Transfer from publisher to consumer between message queue.

//I can deserialise it, because i know the proto message is Person.
PersonProto.Person.parseFrom(bytes);
Run Code Online (Sandbox Code Playgroud)

但是,如果有多个原始消息怎么办?

假设还有另一个原始消息称为Address

syntax = "proto3";

option java_outer_classname = "PersonProto";

message Person {
    int32 id = 2;
    string name = 1;
    string email = 3;
}

message Address {
    string address = 1;
}
Run Code Online (Sandbox Code Playgroud)

当使用者从消息队列接收字节数组时,如何知道它是哪个原始消息?以及如何反序列化字节数组?

Nic*_*olt 8

Protobuf 3引入了Any的概念,可以以类似于@AdamCozzette解释的顶级消息模式的方式工作。

在写方,您将消息打包在Any中

Person person = ...
Any any = Any.pack(person);

out.write(any.toByteArray());
Run Code Online (Sandbox Code Playgroud)

然后在读取侧读入Any并打开您感兴趣的类型:

Any any = Any.parseFrom(in);

if (any.is(Person.class)
{
  Person person = any.unpack(Person.class);
  ...
}
else if (any.is(Address.class);
{
  Address address = any.unpack(Address.class);
  ...
}
else
{
  //Handle unknown message
}
Run Code Online (Sandbox Code Playgroud)

使用Any消除了对特殊消息类型(top-level-message)的需求,而且还消除了类型安全元素,因为您可能会收到使用方代码不知道如何处理的消息。


小智 5

协议缓冲区不是自描述的,所以一般来说,当你得到一个序列化的 protobuf 时,如果不知道预期的模式,就无法解释它的内容。

在您的情况下,我建议使用一个oneof字段。您可以为队列消息使用一个顶级消息类型,并让它包含一个oneof包含人员或地址的字段:

message TopLevelMessage {
  oneof inner_message {
    Person person = 1;
    Address address = 2;
  }
}
Run Code Online (Sandbox Code Playgroud)

消费代码然后需要一个像这样的 switch 语句来检索内部消息:

TopLevelMessage topLevelMessage = TopLevelMessage.parseFrom(...);

switch (topLevelMessage.getInnerMessageCase()) 
{
  case PERSON:
    Person person = topLevelMessage.getPerson();
    ...
    break;

  case ADDRESS:
    Address address = topLevelMessage.getAddress();
    ...
    break;

  default:
     ... 
}
Run Code Online (Sandbox Code Playgroud)