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

时间:2016-11-06 14:39:27

标签: message-queue protocol-buffers

我想使用protobuf代替Json在消息队列之间进行通信。

我知道在只有一条原型信息时如何处理它。

假设proto文件是:

//person.proto
syntax = "proto3";

option java_outer_classname = "PersonProto";

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

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

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);

但是如果有多条原型消息怎么办?

假设有另一个名为Address的原型消息。

syntax = "proto3";

option java_outer_classname = "PersonProto";

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

message Address {
    string address = 1;
}

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

2 个答案:

答案 0 :(得分:4)

Protobuf 3引入了Any的概念,其作用方式与@AdamCozzette解释的顶级消息模式类似。

在写入方,您将邮件打包在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
}

然后在阅读端阅读Any并打开您感兴趣的类型:

dat1 = data.frame(a = c(0.1,0.2,0.3,0.4,0.5), b = c(0.6,0.7,0.8,0.9,0.10), c = c(0.12,0.13,0.14,0.15,0.16), d = c(NA, NA, NA, NA, 0.5))

    a   b    c  d
1 0.1 0.6 0.12 NA
2 0.2 0.7 0.13 NA
3 0.3 0.8 0.14 NA
4 0.4 0.9 0.15 NA
5 0.5 0.1 0.16 0.5

使用Any不需要特殊的消息类型( top-level-message ),但也会删除类型安全元素,因为您可能会收到消息消费代码不知道如何处理。

答案 1 :(得分:2)

协议缓冲区不是自描述的,因此通常当您获得序列化的protobuf时,无法在不知道期望的模式的情况下解释其内容。

在您的情况下,我建议使用oneof字段。您可以为队列消息设置一个顶级消息类型,并使其包含一个包含Person或Address的oneof字段:

message TopLevelMessage {
  oneof inner_message {
    Person person = 1;
    Address address = 2;
  }
}

消费代码需要这样的switch语句才能检索内部消息:

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

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

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

  default:
     ... 
}