2007年发表的Thrift论文,提到自己的优点在于提供了一套语言中立的软件栈,以很低的性能代价获得了很高的开发效率和系统可靠性。核心组件包括Types、Transport、Protocol、Versioning和Processors,Types是指跨语言的类型系统,开发者不用定义任何类型的序列化代码,Transport是指开发者可以灵活定义数据的来源和目的地,可能是网络,可能是内存的片段,也可能是文件,Protocol是指数据的序列化和反序列化过程,开发者通常不用关心,如果有需要也可以定制,Versioning是指协议支持版本,使得客户端可以向前向后兼容,Processors指的是数据的处理器,具体的逻辑由开发者实现。这种灵活的软件栈的代价是,The performance tradeoff incurred by an abstracted I/O layer (roughly one virtual method lookup / function call per operation) was immaterial compared to the cost of actual I/O operations (typically invoking system calls),也就是没什么代价。
展开
9
在路上
2021-10-13
徐老师好,TCompactProtocol处理Delta Encoding的方式非常巧妙,通过判断Field第一个字节的高4位是否为0,得知到底是用了一个还是多个字节存储fieldId和fieldType。
readFieldBegin()的部分源码:
```
// mask off the 4 MSB of the type header. it could contain a field id delta.
short modifier = (short)((type & 0xf0) >> 4);
if (modifier == 0) {
// not a delta. look ahead for the zigzag varint field id.
fieldId = readI16();
} else {
// has a delta. add the delta to the last read field id.
fieldId = (short)(lastFieldId_ + modifier);
}
```
writeFieldBeginInternal()的部分源码:
```
// check if we can use delta encoding for the field id
if (field.id > lastFieldId_ && field.id - lastFieldId_ <= 15) {
// write them together
writeByteDirect((field.id - lastFieldId_) << 4 | typeToWrite);
} else {
// write them separate
writeByteDirect(typeToWrite);
writeI16(field.id);
}
```
直接把IDL写入协议的Header,协议的接收者可以根据Header的信息得知如何解析协议,但是如果每次传输的数据量不大,额外传输的IDL就会成为严重的网络负担。Apache Avro很好的解决了这个问题,在Apache Avro Specification的Protocol Declaration/Protocol Wire Format/Schema Resolution/Parsing Canonical Form for Schemas四个章节中详细地描述了整个过程。
谁负责写数据,就以谁的IDL为准。当客户端第一次发起一种请求时,会先发送一条消息(HandshakeRequest),告知服务端接下来的请求的IDL,服务端会响应消息(HandshakeResponse),告知服务端针对这个请求响应的IDL。之后再发起相同类型的请求时,只需要发送IDL的指纹,指纹对的上,接收方就使用缓存的IDL,如果对不上,接收方会要求发送方重发Handshake。哪些内容构成了一个IDL的指纹呢?并非整个文本,因为在文本中增加一个空格,调整字段的书写顺序,并不影响数据的序列化和反序列化,只有真正影响序列化和反序列化的内容,才会被当作计算指纹的一部分。