如何知道字节数组是哪个 protobuf 消息?
How to know which protobuf message the byte array is?
我想使用 protobuf 而不是 Json 在消息队列之间进行通信。
我知道proto消息只有一个的时候怎么处理
假设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;
}
当消费者从消息队列中收到字节数组时,如何知道它是哪个原型消息?以及如何反序列化字节数组?
协议缓冲区不是自描述的,因此通常当您获得序列化的 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:
...
}
Protobuf 3 引入了 Any 的概念,它的行为方式类似于 @AdamCozzette 解释的 top-level-message 模式。
在写入端,您将消息打包成 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 消除了对特殊消息类型(顶级消息)的需要,但同时也消除了类型安全元素因为您可能会收到消费代码不知道如何处理的消息。
我想使用 protobuf 而不是 Json 在消息队列之间进行通信。
我知道proto消息只有一个的时候怎么处理
假设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;
}
当消费者从消息队列中收到字节数组时,如何知道它是哪个原型消息?以及如何反序列化字节数组?
协议缓冲区不是自描述的,因此通常当您获得序列化的 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:
...
}
Protobuf 3 引入了 Any 的概念,它的行为方式类似于 @AdamCozzette 解释的 top-level-message 模式。
在写入端,您将消息打包成 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 消除了对特殊消息类型(顶级消息)的需要,但同时也消除了类型安全元素因为您可能会收到消费代码不知道如何处理的消息。