Thrift TCompactProtocol 反序列化分配大对象

2024-01-17 11:36

本文主要是介绍Thrift TCompactProtocol 反序列化分配大对象,希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!

Thrift

Apache Thrift是一个软件框架,用于可扩展的跨语言服务开发。它拥有强大的代码生成引擎,支持多种编程语言,如C++、Java、Python等。Apache Thrift允许定义一个简单的文件(后缀名以.thrift结尾),其中包含命名空间、数据类型和服务接口。通过Apache Thrift的编译器,可以自动将定义的接口文件编译生成代码,以便 RPC 客户端和服务器端调用自动生成的接口代码。

Apache Thrift的主要优点包括:

  1. 开发速度快:通过编写RPC接口Thrift IDL(接口描述语言)文件,利用编译生成器自动生成服务端骨架(Skeletons)和客户端桩(Stubs)。客户端只需要拷贝IDL定义好的客户端桩和服务对象,然后就像调用本地对象的方法一样调用远端服务。
  2. 接口维护简单:通过维护Thrift格式的IDL文件(注意写好注释),即可作为给Client使用的接口文档使用,也自动生成接口代码,始终保持代码和文档的一致性。
  3. 灵活支持接口的可扩展性:Thrift协议可灵活支持接口的可扩展性。

然而,Apache Thrift也有一些缺点,例如:

  1. 架构复杂:需要关注服务的定义,而不必关心protocol和transport的具体实现,这可能增加了架构的复杂性。
  2. 缺乏社区支持:虽然Apache Thrift已经相当成熟和稳定,但其社区支持和活跃度相对较低

Thrift TCompactProtocol 反序列化分配大对象

案例引自 记一次使用gdb诊断gc问题全过程

查看GC监控,发现GC有一些尖峰,有时会突然有大量的内存分配。若是内存不足,还可能会导致 OOM ——java.lang.OutOfMemoryError: Requested array size exceeds VM limit

排查后发现:大对象分配由readBinary方法发起

org.apache.thrift.protocol.TCompactProtocol#readBinary():读取二进制数组类型数据,返回 ByteBuffer 对象

  public ByteBuffer readBinary() throws TException {int length = readVarint32();checkStringReadLength(length);if (length == 0) return EMPTY_BUFFER;if (trans_.getBytesRemainingInBuffer() >= length) {ByteBuffer bb = ByteBuffer.wrap(trans_.getBuffer(), trans_.getBufferPosition(), length);trans_.consumeBuffer(length);return bb;}byte[] buf = new byte[length]; // 错误地读取长度trans_.readAll(buf, 0, length);return ByteBuffer.wrap(buf);}

解决方式:readBinary()中会校验stringLengthLimit,超过配置值会抛出TProtocolException。故在初始化TCompactProtocol.Factory()可以设置stringLengthLimit参数,可以避免异常情况导致错误的大对象分配问题

0.12.0 版本 libthrift 支持设置stringLengthLimitcontainerLengthLimit参数,较低版本如 0.6.1 不支持

  /*** TProtocolFactory that produces TCompactProtocols.*/public static class Factory implements TProtocolFactory {private final long stringLengthLimit_;private final long containerLengthLimit_;public Factory() {this(NO_LENGTH_LIMIT, NO_LENGTH_LIMIT);}public Factory(long stringLengthLimit) {this(stringLengthLimit, NO_LENGTH_LIMIT);}public Factory(long stringLengthLimit, long containerLengthLimit) {this.containerLengthLimit_ = containerLengthLimit;this.stringLengthLimit_ = stringLengthLimit;}public TProtocol getProtocol(TTransport trans) {return new TCompactProtocol(trans, stringLengthLimit_, containerLengthLimit_);}}

错误对象的反序列化可能会导致这个问题,下列场景也可能会错误地读取长度,导致大对象的分配

潜在异常场景

TCompactProtocol#readBinary 是 Apache Thrift 中用于读取二进制数据的方法。这个方法可能会在以下情况下错误地读取长度:

  1. 数据损坏或格式错误:如果二进制数据流被损坏或格式不正确,readBinary 函数可能无法正确解析长度信息。这可能是由于网络传输错误、文件损坏、或者数据序列化错误导致的
  2. 内存不足:如果尝试读取的数据量超过了应用程序或系统分配的内存容量,readBinary 函数可能会失败。这可能是因为系统的可用内存不足,或者应用程序的内存限制被设置得太低
  3. 长度字段格式错误:如果长度字段的格式不正确(例如,长度字段的编码方式与 TCompactProtocol 预期的不匹配),那么 readBinary 函数可能无法正确解析长度信息
  4. 协议版本不匹配:如果客户端和服务器使用的Thrift协议版本不一致,可能会导致解析错误,包括对长度字段的解析
  5. 输入流错误:如果提供给 readBinary 方法的输入流是错误的或已关闭,该方法可能会抛出异常或返回不正确的结果
  6. 自定义类型和序列化问题:如果Thrift定义中使用了自定义类型,并且这些类型的序列化有问题,那么在解析长度和数据时可能会出错
  7. 编码和解码问题:如果数据的编码和解码方式不匹配,或者使用了错误的解码方式,可能会导致解析错误
  8. 其他原因:还可能有其他一些原因导致 readBinary 方法无法正确读取长度,例如底层I/O错误、超时、网络中断等

在网络传输过程中,如果数据包的大小超过了网络传输的限制,可能需要将其分割成多个较小的分片进行发送。具体的分片大小限制取决于使用的网络协议和网络设备的规范。例如,在使用TCP传输时,TCP协议通常会根据网络的最大传输单元(Maximum Transmission Unit,MTU)来确定数据包的大小限制。通常情况下,MTU的默认值为1500字节

TBinaryProtocol vs TCompactProtocol

在 Thrift 中,可以使用不同的协议来进行数据的序列化和反序列化,其中 TBinaryProtocol 和 TCompactProtocol 是两种常用的协议。下面是这两种协议的简单对比:

  1. 存储空间和带宽效率:

    • TBinaryProtocol:该协议以二进制格式存储数据,相对较宽松,因此在存储空间和带宽效率上不如 TCompactProtocol。
    • TCompactProtocol:该协议采用紧凑的二进制格式,对数据进行压缩,因此相比 TBinaryProtocol 更节省存储空间和带宽。
  2. 兼容性:

    • TBinaryProtocol:由于其宽松的格式,它在不同版本的 Thrift 之间具有更好的兼容性。
    • TCompactProtocol:由于其紧凑的格式,它在不同版本的 Thrift 之间的兼容性可能较差。
  3. 性能:

    • TBinaryProtocol:由于其简单的实现和较低的开销,在某些情况下可能提供更好的性能。
    • TCompactProtocol:尽管其提供了更好的压缩和带宽效率,但由于其更复杂的实现和可能的解压缩开销,性能可能略逊于 TBinaryProtocol。
  4. 流行度:

    • TBinaryProtocol:由于其较好的兼容性和性能,在一些早期和现有的项目中更受欢迎。
    • TCompactProtocol:由于其带宽和存储空间的效率,在一些需要优化性能的新兴应用中更受欢迎。
  5. 版本差异:

    • TBinaryProtocol:二进制协议的版本差异可能更容易识别和处理,因为每个字段的前两个字节都包含了字段的长度信息。
    • TCompactProtocol:紧凑协议的版本差异可能更难以识别和处理,因为该协议通过改变字节顺序和类型来表示版本差异。
  6. 用途:

    • TBinaryProtocol:通常用于需要高兼容性和简单实现的情况。
    • TCompactProtocol:通常用于需要高带宽和存储效率的情况,例如移动应用或实时系统。

参考资料:

  1. Apache Thrift
  2. 记一次使用gdb诊断gc问题全过程
  3. Thrift反序列化导致OOM

这篇关于Thrift TCompactProtocol 反序列化分配大对象的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!



http://www.chinasem.cn/article/615894

相关文章

SpringMVC高效获取JavaBean对象指南

《SpringMVC高效获取JavaBean对象指南》SpringMVC通过数据绑定自动将请求参数映射到JavaBean,支持表单、URL及JSON数据,需用@ModelAttribute、@Requ... 目录Spring MVC 获取 JavaBean 对象指南核心机制:数据绑定实现步骤1. 定义 Ja

Python打印对象所有属性和值的方法小结

《Python打印对象所有属性和值的方法小结》在Python开发过程中,调试代码时经常需要查看对象的当前状态,也就是对象的所有属性和对应的值,然而,Python并没有像PHP的print_r那样直接提... 目录python中打印对象所有属性和值的方法实现步骤1. 使用vars()和pprint()2. 使

MySQL JSON 查询中的对象与数组技巧及查询示例

《MySQLJSON查询中的对象与数组技巧及查询示例》MySQL中JSON对象和JSON数组查询的详细介绍及带有WHERE条件的查询示例,本文给大家介绍的非常详细,mysqljson查询示例相关知... 目录jsON 对象查询1. JSON_CONTAINS2. JSON_EXTRACT3. JSON_TA

C#之List集合去重复对象的实现方法

《C#之List集合去重复对象的实现方法》:本文主要介绍C#之List集合去重复对象的实现方法,具有很好的参考价值,希望对大家有所帮助,如有错误或未考虑完全的地方,望不吝赐教... 目录C# List集合去重复对象方法1、测试数据2、测试数据3、知识点补充总结C# List集合去重复对象方法1、测试数据

Java中JSON格式反序列化为Map且保证存取顺序一致的问题

《Java中JSON格式反序列化为Map且保证存取顺序一致的问题》:本文主要介绍Java中JSON格式反序列化为Map且保证存取顺序一致的问题,具有很好的参考价值,希望对大家有所帮助,如有错误或未... 目录背景问题解决方法总结背景做项目涉及两个微服务之间传数据时,需要提供方将Map类型的数据序列化为co

RedisTemplate默认序列化方式显示中文乱码的解决

《RedisTemplate默认序列化方式显示中文乱码的解决》本文主要介绍了SpringDataRedis默认使用JdkSerializationRedisSerializer导致数据乱码,文中通过示... 目录1. 问题原因2. 解决方案3. 配置类示例4. 配置说明5. 使用示例6. 验证存储结果7.

Spring中管理bean对象的方式(专业级说明)

《Spring中管理bean对象的方式(专业级说明)》在Spring框架中,Bean的管理是核心功能,主要通过IoC(控制反转)容器实现,下面给大家介绍Spring中管理bean对象的方式,感兴趣的朋... 目录1.Bean的声明与注册1.1 基于XML配置1.2 基于注解(主流方式)1.3 基于Java

C++/类与对象/默认成员函数@构造函数的用法

《C++/类与对象/默认成员函数@构造函数的用法》:本文主要介绍C++/类与对象/默认成员函数@构造函数的用法,具有很好的参考价值,希望对大家有所帮助,如有错误或未考虑完全的地方,望不吝赐教... 目录名词概念默认成员函数构造函数概念函数特征显示构造函数隐式构造函数总结名词概念默认构造函数:不用传参就可以

C++类和对象之默认成员函数的使用解读

《C++类和对象之默认成员函数的使用解读》:本文主要介绍C++类和对象之默认成员函数的使用方式,具有很好的参考价值,希望对大家有所帮助,如有错误或未考虑完全的地方,望不吝赐教... 目录一、默认成员函数有哪些二、各默认成员函数详解默认构造函数析构函数拷贝构造函数拷贝赋值运算符三、默认成员函数的注意事项总结一

SpringBoot实现Kafka动态反序列化的完整代码

《SpringBoot实现Kafka动态反序列化的完整代码》在分布式系统中,Kafka作为高吞吐量的消息队列,常常需要处理来自不同主题(Topic)的异构数据,不同的业务场景可能要求对同一消费者组内的... 目录引言一、问题背景1.1 动态反序列化的需求1.2 常见问题二、动态反序列化的核心方案2.1 ht