首页 文章

protobuf-net消息序列化大小属性

提问于
浏览
8

我们使用protobuf-net对公共协议基于Google Protocol Buffers的应用程序中的消息进行序列化和反序列化 . 该库非常出色,涵盖了除此之外的所有要求:我们需要在消息实际序列化之前找出序列化的消息长度(以字节为单位) .

The question已经在一年半前被问过了,根据Marc的说法,唯一的方法是序列化到MemoryStream并随后读取 .Length 属性 . 在我们的例子中这是不可接受的,因为MemoryStream在幕后分配一个字节缓冲区,我们必须避免这种情况 .

来自同一回复的这一行给了我们希望它毕竟是可能的:

如果你澄清用例是什么,我相信我们可以轻松地提供它(如果还没有) .

这是我们的用例 . 我们有大小在几个字节和两兆字节之间变化的消息 . 应用程序预先分配用于套接字操作和序列化/反序列化的字节缓冲区,一旦预热阶段结束,就不能创建额外的缓冲区(提示:avoding GC和堆碎片) . 字节缓冲区基本上是合并的 . 我们还希望尽可能避免在缓冲区/流之间复制字节 .

我们提出了两种可能的策略,它们都需要预先设置邮件大小:

  • 使用(大)固定大小的字节缓冲区并序列化可以放入一个缓冲区的所有消息;使用 Socket.Send 发送缓冲区的内容 . 我们必须知道下一条消息何时无法放入缓冲区并停止序列化 . 如果没有消息大小,实现此目的的唯一方法是等待在 Serialize 期间发生异常 .

  • 使用(小)可变大小的字节缓冲区并将每条消息序列化为一个缓冲区;使用 Socket.Send 发送缓冲区的内容 . 为了从池中检出具有适当大小的字节缓冲区,我们需要知道序列化消息有多少字节 .

因为协议已经定义(我们不能改变它)并且要求消息长度前缀为Varint32,所以我们不能使用 SerializeWithLengthPrefix 方法 .

那么是否可以添加一种估计消息大小而无需序列化到流中的方法?如果它不适合图书馆的当前功能集和路线图,但是可行,我们有兴趣自己扩展图书馆 . 我们也在寻找替代方法,如果有的话 .

1 回答

  • 4

    如上所述,这不是立即可用的,因为代码故意尝试对数据进行单次传递(尤其是 IEnumerable<T> 等) . 但是,根据您的数据,它可能已经进行了适量的复制,以允许子消息也具有长度前缀,因此可能需要进行杂耍 . 通过在消息内部使用"grouped"子格式可以大大减少这种杂耍,因为组允许仅向前构造而不带回溯 .

    那么是否可以添加一种估算邮件大小而无需序列化到流中的方法?

    估计是无用的;因为没有终结者,所以需要准确 . 最终,如果没有真正做到这一点,尺寸有点难以预测 . 在v1中有一些代码用于大小预测,但单通道代码目前似乎是首选,并且在大多数情况下缓冲器开销是标称的(有代码可以重新使用内部缓冲区,因此它不会花费全部为小消息分配缓冲区的时间) .

    如果你的消息内部只是前向(分组),那么作弊可能是序列化为测量的假流,但丢弃所有数据;但是,你最终会序列化两次 .

    回覆:

    并且要求消息长度前缀为Varint32,我们不能使用SerializeWithLengthPrefix方法

    我不太确定我看到那里的关系 - 它允许在这里使用一系列格式等;也许你可以更具体一点?

    重新复制数据 - 我在这里玩的想法是使用长度前缀的子标准形式 . 例如,在大多数情况下,5个字节可能是充足的,因此它可能会留下5个字节,然后简单地覆盖 without 凝聚(因为八位字节 10000000 仍然意味着"zero and continue",即使它是多余的) . 这仍然需要缓冲(以允许回填),但不需要和移动数据 .

    最后一个简单的想法很简单:序列化为 FileStream ;然后写入文件长度和文件数据 . 显然,它会交换IO的内存使用量 .

相关问题