Thrift 如何处理被拆分为多个消息的 Zlib 刷新标记?

Posted

技术标签:

【中文标题】Thrift 如何处理被拆分为多个消息的 Zlib 刷新标记?【英文标题】:How does Thrift handle Zlib flush markers being split over multiple messages? 【发布时间】:2018-08-10 10:15:07 【问题描述】:

我有一个应用程序,它有一个 c++ 服务器和一个使用 Apache Thrift 的 c# 客户端。我在服务器上使用TZlibTransport.cpp 进行zlib 压缩,并使用Ionic.Zlib 来解压缩客户端中的数据的包装器,这在大多数情况下都有效。

我注意到在非常特殊的情况下,客户端会因以下错误之一而崩溃:

Thrift.Protocol.TProtocolException: Missing version in readMessageBegin, old client?
   at Thrift.Protocol.TBinaryProtocol.ReadMessageBegin()

Ionic.Zlib.ZlibException: Bad state (invalid block type)
   at Ionic.Zlib.InflateManager.Inflate(FlushType flush)

我发现在发生这些错误的所有情况下,服务器都在发送两个包,一个刚刚超过 1024 字节(这是 TZlibTransport.cpp 使用的 compressed write buffer 的大小),另一个是 5- 8 个字节。看第二个包的数据,发现是zlib使用的flush标记,加了两次,

ff ff 00 00 00 ff ff

第一个标记的第一部分位于前一个包的末尾。如果我稍微增加缓冲区的大小,使其有足够的空间在一个包中写入标记,则不会发生崩溃,所以我认为是这个标记被添加了两次导致问题。但是,仅更改此缓冲区大小并不是解决方案,因为这意味着错误发生在应用程序的其他位置。

我查看了 zlib,发现如果缓冲区中没有足够的空间,这是预期的行为 (https://github.com/madler/zlib/issues/149)。但是,我找不到任何遇到过这种情况的人,这会导致节俭问题。

因此,我的问题是,对于特定的数据长度,thrift 是否可以将标记拆分到多个包中,以及客户端应该如何处理这个问题。

【问题讨论】:

【参考方案1】:

看起来问题不在于标记被发射了两次,而只是第一个标记没有完全适合缓冲区。如果输出只是ff ff,您将遇到完全相同的问题和相同的错误消息。 ff 无法启动 deflate 流,因为它提供了无效的块类型 (3)。

从您的描述看来,Thrift 中存在一个错误,即它不能保证和/或检查所有压缩数据是否真正适合缓冲区。

【讨论】:

感谢您的回复,帮助我了解出了什么问题。在这种情况下,是否应该用零填充第一个包,以便所有标记都在第二个包中?这会导致 zlib 出现任何问题吗? @ldsrc:您能否提交JIRA ticket 并(如果可能)添加拉取请求?那真是太棒了! @JensG 我已经为它提交了一张 JIRA 票,THRIFT-4620。 @ldsrc 为了确保当前的 deflate 块在字节边界上结束,发出一个空的存储块,它是三个零位,生成的最后一个字节根据需要用零填充以到达字节边界,然后是字节 00 00 ff ff。为了使单个数据包的膨胀操作正确完成并处于下一个块中字节的状态,所有这些都应该适合前一个数据包。用零填充不是 inflate 期望或将处理的事情。底线:数据包需要更大,或者压缩的数据量需要更少才能适合。

以上是关于Thrift 如何处理被拆分为多个消息的 Zlib 刷新标记?的主要内容,如果未能解决你的问题,请参考以下文章

putExtra:我应该如何处理大型数组?数据库或拆分为更小的阵列?

Thrift安装介绍

sql导入数据时总是报错,详见下图,请大神指导如何处理?文件格式改过多个版本,也没有空格或符号

如何处理自动模块中的拆分包?

T-SQL:与字符串连接相反-如何将字符串拆分为多个记录[重复]

深度学习中如何处理整张幻灯片图像