【问题标题】:How to know which protobuf message the byte array is?如何知道字节数组是哪个 protobuf 消息?
【发布时间】:2017-03-19 22:24:03
【问题描述】:

我想使用 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);

但是如果有多个 proto 消息怎么办?

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

syntax = "proto3";

option java_outer_classname = "PersonProto";

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

message Address {
    string address = 1;
}

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

【问题讨论】:

    标签: message-queue protocol-buffers


    【解决方案1】:

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

    在写入端,您将消息打包到Any

    Person person = ...
    Any any = Any.pack(person);
    
    out.write(any.toByteArray());
    

    然后在读取端读取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 消除了对特殊消息类型(顶级消息)的需求,但也消除了您可能收到的消息中的类型安全元素消费代码不知道怎么处理。

    【讨论】:

    【解决方案2】:

    协议缓冲区不是自描述的,因此通常当您获得序列化的 protobuf 时,如果不知道预期的架构,就无法解释其内容。

    在您的情况下,我建议使用 oneof 字段。您可以为队列消息设置一个顶级消息类型,并让它包含一个包含人员或地址的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:
         ... 
    }
    

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 2014-08-05
      • 2013-07-09
      • 1970-01-01
      • 2011-11-16
      • 1970-01-01
      • 2012-06-28
      • 2019-10-12
      相关资源
      最近更新 更多