Apache Thrift
是一个软件框架,用于可扩展的跨语言服务开发。它拥有强大的代码生成引擎,支持多种编程语言,如C++、Java、Python等。Apache Thrift
允许定义一个简单的文件(后缀名以.thrift结尾),其中包含命名空间、数据类型和服务接口。通过Apache Thrift
的编译器,可以自动将定义的接口文件编译生成代码,以便 RPC 客户端和服务器端调用自动生成的接口代码。
Apache Thrift的主要优点包括:
然而,Apache Thrift也有一些缺点,例如:
案例引自 记一次使用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 支持设置
stringLengthLimit
及containerLengthLimit
参数,较低版本如 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 中用于读取二进制数据的方法。这个方法可能会在以下情况下错误地读取长度:
在网络传输过程中,如果数据包的大小超过了网络传输的限制,可能需要将其分割成多个较小的分片进行发送。具体的分片大小限制取决于使用的网络协议和网络设备的规范。例如,在使用TCP传输时,TCP协议通常会根据网络的最大传输单元(Maximum Transmission Unit,MTU)来确定数据包的大小限制。通常情况下,MTU的默认值为1500字节
在 Thrift 中,可以使用不同的协议来进行数据的序列化和反序列化,其中 TBinaryProtocol 和 TCompactProtocol 是两种常用的协议。下面是这两种协议的简单对比:
存储空间和带宽效率:
兼容性:
性能:
流行度:
版本差异:
用途:
参考资料: