ZeroMQ与Protocol Buffers

34

ZeroMQ FAQ页面建议使用Google的protobuf作为序列化消息内容的方法。

有人看到过一个好的使用示例吗?

我还需要回答“序列化消息的最大优势是什么?” - 是否可能是我可以放弃的东西,从而利用更轻便的管道的优势。

我非常喜欢.proto文件和protoc编译器的想法。

此外,似乎另一个很棒的工具可以用于这个场景是libev,欢迎任何评论 :)


以防万一你问,我正在寻找一个可扩展的网络音乐演奏系统(这意味着实时控制,不涉及音频流)。好吧,我想解决一些OSC协议并没有真正解决的问题。这是说,如果有人读到这篇文章确实熟悉这个特定主题,但问题应该更普遍。 - errordeveloper
如果有人感兴趣,这里是一个使用0MQ + protobuf的经验分享(2015年)。 - Brian Dolan
4个回答

28

如果您可以百分之百确定将要在 ZMQ 上通信的程序始终能够理解彼此的二进制格式(例如,因为它们总是一起分发并且都使用相同的编译器选项进行编译),则我认为添加序列化会增加不必要的开销。

一旦无法满足上述条件(例如运行在不同主机类型上的伙伴程序、使用不同语言编写的程序,甚至是可能独立演化的伙伴程序 - 这可能导致其原始二进制结构不兼容),序列化几乎肯定成为必须。

现在似乎每个人都在创建序列化解决方案,这可能表明没有一个大小适合所有的解决方案。 这个页面 包含了对 27 种不同序列化系统的序列化时间、反序列化时间和大小进行了详尽的基准测试。不要跳过该页面的第一个段落,它说“警告,基准测试可能会误导”。对于您来说,您的应用程序和数据是最重要的,但那里呈现的数据可能有助于您缩小您想要详细研究的选择范围。


1
是的,它可能需要在不同的架构上运行,所以我选择了 msgpack :) - errordeveloper
有趣的回答。您的评论“我看不到序列化所添加的开销带来的任何好处。”引起了我的兴趣。您打算如何在传输数据时避免支付某种形式的计算开销?无论是将消息编码为JSON还是使用其他库,比如协议缓冲区,仍然会有计算成本。您的答案表明可以避免这种情况。怎么做? - Stewart
@Stewart 在这个语句之前,确切的条件已经被阐述了 :) :如果发送端和接收端使用完全相同的二进制格式,那么序列化就没有任何好处。当然,这是一个非常苛刻的要求,但指出这一点似乎很有用。 - fvu
@fvu 我从未以这种方式使用过二进制。我在这个主题上又提了一个问题。很想听听你的答案。https://stackoverflow.com/questions/53908376/i-have-two-web-services-with-the-same-binary-format-can-i-avoid-paying-encoding - Stewart

23

这是一个通过Java和C++发送和接收消息的示例:

在Java中进行序列化:

Person person = Person.newBuilder().setName("chand")
    .setEmail("chand@test.com").setId(55555).build();
socket.send(person.toByteArray(), 0);

Java中的反序列化:

byte[] reply = socket.recv(0);
Person person2 = Person.parseFrom(reply);

C++中的序列化:

Person p = Person();
std::string str;
p.SerializeToString(&str);
int sz = str.length();
zmq::message_t *query = new message_t(sz);
memcpy(query->data (), str.c_str(), sz);
socket->send (*query);

C++中的反序列化

zmq::message_t resultset(100);
socket->recv (&resultset);

Person p = Person();
p.ParseFromArray(resultset.data(), resultset.size());
printf("\n Server : %s", p.name().c_str());

将一个 zmq 消息切换为指向 zmq 消息的指针,就像您展示的那样,解决了我在发送 protobuf 消息时遇到的 zmq 断言错误。 - matttm

4

8
使用发布订阅模式时,第一个 ZMQ 帧应该是订阅主题。而第二个、第三个、......第n个帧可以是 protobuf 数据。 - Schildmeijer

2

在通信时,您总是需要进行序列化。结构体是随机访问的。像ZeroMQ这样的通信层是串行的。

您可以使用语言自带的“默认序列化”。例如,在C++中,没有指针的结构体将具有特定的二进制布局,可以直接转换为字节数组。这个二进制布局间接地成为了您的序列化层,并且是语言和编译器特定的。

只要限制自己使用没有指针的结构体,并在管道的两端使用相同的编译器和语言...就可以避免使用在默认布局之上进行额外序列化的库。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接