Thrift之TProtocol类体系原理及源码详细解析之紧凑协议类TCompactProtocolT(TCompactProtocol).doc_第1页
Thrift之TProtocol类体系原理及源码详细解析之紧凑协议类TCompactProtocolT(TCompactProtocol).doc_第2页
Thrift之TProtocol类体系原理及源码详细解析之紧凑协议类TCompactProtocolT(TCompactProtocol).doc_第3页
Thrift之TProtocol类体系原理及源码详细解析之紧凑协议类TCompactProtocolT(TCompactProtocol).doc_第4页
Thrift之TProtocol类体系原理及源码详细解析之紧凑协议类TCompactProtocolT(TCompactProtocol).doc_第5页
已阅读5页,还剩4页未读 继续免费阅读

下载本文档

版权说明:本文档由用户提供并上传,收益归属内容提供方,若内容存在侵权,请进行举报或认领

文档简介

这个协议类采用了zigzag编码,这种编码是基于Variable-lengthquantity编码提出来的,因为Variable-lengthquantity编码对于负数的编码都需要很长的字节数,而zigzag编码对于绝对值小的数字,无论正负都可以采用较少的字节来表示,充分利用了Varint技术。所以这个协议类采用zigzag编码可以节省传输空间,使数据的传输效率更高。至于zigzag具体的编码实现方式可以网上查查,其实就是把从低位到最后一个还存在1(二进制)的最高位表示出来就可以了。这个协议类对外提供的方法和上面介绍的二进制协议相同,这样可以很方便使用者从一种协议改变到另一种协议。下面我同样结合scribe提供的Log方法来分析这个协议类的功能,不过不会像上面二进制协议在把整个过程分析了,我只会分析与协议相关的部分了,分析一些比较难懂的一些函数功能,分析的思路还是按照函数调用过程来分析。首先还是分析writeMessageBegin函数,下面是这个函数的实现代码: 1 template uint32_t TCompactProtocolT:writeMessageBegin( 2 3 const std:string& name, const TMessageType messageType, const int32_t seqid) 4 5 uint32_t wsize = 0; 6 7 wsize += writeByte(PROTOCOL_ID);/写入这个协议的产品ID号:为0x82 8 9 wsize += writeByte(VERSION_N & VERSION_MASK) | (int32_t)messageType TYPE_SHIFT_AMOUNT) & TYPE_MASK);/写入此协议的版本号和消息类型:前3位是消息类型,后面5位是协议版本号10 11 wsize += writeVarint32(seqid);/写入请求序列号12 13 wsize += writeString(name);/写入消息名称(也就是函数调用名称)14 15 return wsize;/返回写入的大小,多少字节16 17 因为这些协议类都是模板类,所以每一个函数也就是模板函数了。函数具体的功能代码里有详细注释,其中的writeByte函数就是写入一个字节到服务器。这里与二进制协议不同的是这里写入请求序列号(也就是对于所有的整型数)都调用的writeVarint32函数,这个函数就是采用zigzag编码写入整型数到服务器,代码如下: 1 template uint32_t TCompactProtocolT:writeVarint32(uint32_t n) 2 3 uint8_t buf5;/对于一个整数,zigzag编码最大采用5个字节保存 4 5 uint32_t wsize = 0; 6 7 while (true) 8 9 if (n & 0x7F) = 0) /判断除了最低7位是否还有其他高位为1(二进制)10 11 bufwsize+ = (int8_t)n;/没有了代表着就是最后一个字节12 13 break;/退出循环14 15 else 16 17 bufwsize+ = (int8_t)(n & 0x7F) | 0x80);/取最低7位加上第8位(为1代表后续还有字节属于这个整数,为0代表这是这个整数的最后一个字节了。18 19 n = 7;/移走已经编码的位数20 21 22 23 24 25 trans_-write(buf, wsize);/写入编码的字节数26 27 return wsize;/返回写入的字节数28 29 这个函数的功能就是对整数进行Variable-lengthquantity编码后写入,如果为负数需要处理。如果不处理那么每一个负数都需要5个字节来编码,因为最高位表示符号位,而负数的符号位用1表示(也就是说负数的最高位永远为1)。处理的方式也很简单(就是zigzag编码),就是把最高位(符号位)移动到最低位,最低位到次高位一次向高位移动一位,代码如下(就一句就实现了):1 template 2 3 uint32_t TCompactProtocolT:i32ToZigzag(const int32_t n) 4 5 return (n 31);6 7 上面写入整数和处理负数都是针对的32位的,当然也有64位的相应函数,实现方式相同。我们在回到writeMessageBegin函数,里面还有一个writeString函数用来写入一个字符串的,与二进制不同的是写入字符串长度也是采用了可变长度编码的方式写入,然后写入字符串的具体数据,它是调用另一个函数writeBinary写入,writeBinary实现代码如下: 1 template 2 3 uint32_t TCompactProtocolT:writeBinary(const std:string& str) 4 5 uint32_t ssize = str.size(); 6 7 uint32_t wsize = writeVarint32(ssize) + ssize;/写入字符串的长度并计算写入的长度(包括字符串的长度) 8 9 trans_-write(uint8_t*)str.data(), ssize);/写入字符串的数据10 11 return wsize;12 13 写消息函数分析完毕以后我们在来看看对应的读消息函数readMessageBegin,看这个函数必须和写入消息的函数对应起来看,不然就不能理解它读取和处理的流程代码,具体实现如下代码: 1 template uint32_t TCompactProtocolT:readMessageBegin( 2 3 std:string& name, TMessageType& messageType, int32_t& seqid) 4 5 uint32_t rsize = 0; 6 7 int8_t protocolId; 8 9 int8_t versionAndType;10 11 int8_t version;12 13 rsize += readByte(protocolId);/读取协议产品ID号14 15 if (protocolId != PROTOCOL_ID) /判断是不是这个协议的产品ID号,不是就抛出异常16 17 throw TProtocolException(TProtocolException:BAD_VERSION, Bad protocol identifier);18 19 20 21 rsize += readByte(versionAndType);/读取此协议的版本号和消息类型22 23 version = (int8_t)(versionAndType & VERSION_MASK);/取出协议版本号24 25 if (version != VERSION_N) /判断是不是对应的协议版本号,不是抛出异常26 27 throw TProtocolException(TProtocolException:BAD_VERSION, Bad protocol version);28 29 30 31 messageType = (TMessageType)(versionAndType TYPE_SHIFT_AMOUNT) & 0x03);/取出消息类型32 33 rsize += readVarint32(seqid);/读取请求序列号34 35 rsize += readString(name);/读取消息名称(函数名称)36 37 return rsize;/返回读取的长度(字节)38 39 通过对照写入消息的函数就很容易理解,因为你写入什么我就读什么并且判断是不是相同协议写入的,具体分析可以看上面的代码和详细的注释。而且还有一点就是具体的写入数据类型的函数也是采用对应类型的读函数,例如读可变长整型写入就是采用可变长读函数readVarint32,写字符串对应读字符串函数readString,对照相应的写入函数来看这些读数据函数就非常好理解了,就不具体分析这些读函数了。下面在分析几个复合数据类型的写入函数,因为这些写入函数存在一定技巧不容易(或者说不那么直观吧)理解清楚。首先看看struct类型的数据写入的过程,它分为写入开始、中间处理和写入结束。下面是开始写入struct的代码: 1 template 2 3 uint32_t TCompactProtocolT:writeStructBegin(const char* name) 4 5 (void) name; 6 7 lastField_.push(lastFieldId_);/把最后写入的字段ID压入堆栈 8 9 lastFieldId_ = 0;/重新设置为010 11 return 0;12 13 这开始写入的函数没有做什么具体的工作,只是把最后写入的字段ID压入堆栈,这样做的目的是处理那种struct嵌套的数据结构类型。Struct里面的是一个一个的字段,所以根据struct的字段个数分别调用字段写入函数依次写入,字段写入函数定义如下: 1 template int32_t TCompactProtocolT:writeFieldBeginInternal( 2 3 const char* name, const TType fieldType, const int16_t fieldId, int8_t typeOverride) 4 5 (void) name;/为了防止编译器产生警告信息 6 7 uint32_t wsize = 0; 8 9 / 如果存在对于对应的类型就转换为对应的10 11 int8_t typeToWrite = (typeOverride = -1 ? getCompactType(fieldType) : typeOverride);12 13 / 检查字段ID是否使用了增量编码14 15 if (fieldId lastFieldId_ & fieldId - lastFieldId_ = 15) /如果使用了增量编码并增量且小于等于1516 17 wsize += writeByte(fieldId - lastFieldId_) 4 | typeToWrite);/字段ID和数据类型一起写入18 19 else /否则单独写入20 21 wsize += writeByte(typeToWrite);/写入数据类型22 23 wsize += writeI16(fieldId);/写入字段ID24 25 26 27 lastFieldId_ = fieldId;/保存写入字段ID为最后一个写入的ID28 29 return wsize;/返回写入的长度30 31 当结构体里面的每一个字段都写入以后还需要调用writeStructEnd函数来处理结束一个struct的写入,主要处理是字段ID的相关内容,实现代码如下:1 template uint32_t TCompactProtocolT:writeStructEnd() 2 3 lastFieldId_ = lastField_.top();/取得最后一次压入堆栈的字段ID号4 5 lastField_.pop();/弹出以取得的字段ID6 7 return 0;8 9 同样的结构体也有对应的读取函数,具体实现就不在具体分析了!下面继续分析一些特殊的处理代码,首先看看负数在进行zigzag编码前怎样处理,对于32位和64位都是一句代码就搞定,如下代码:1 return (n 1) -(n & 1);这句代码的作用就是把最高位的符号位移动到最低位,然后最低位到次高位依次向高位移动一位,这样就避免了所有负数都需要最长的字节来编码。在看看读可变长编码写入整型数的函数,32位和64位都是相同的实现,因为32位也是调用64位的函数实现的,实现代码如下: 1 template uint32_t TCompactProtocolT:readVarint64(int64_t& i64) 2 3 uint32_t rsize = 0; 4 5 uint64_t val = 0; 6 7 int shift = 0; 8 9 uint8_t buf10; / 64 位采用zigzag编码最长可能是10字节10 11 uint32_t buf_size = sizeof(buf);12 13 const uint8_t* borrowed = trans_-borrow(buf, &buf_size);/并不是所有transport都支持 14 15 if (borrowed != NULL) / 快路径,要读的数据已经在缓存中16 17 while (true) 18 19 uint8_t byte = borrowedrsize;20 21 rsize+;22 23 val |= (uint64_t)(byte & 0x7f) consume(rsize);/消耗了多少字节,即表示这个编码用了多少字节32 33 return rsize;34 35 36 37 / 检查编码数据是否超过了最长限制,是就抛出一个无效的异常38 39 if (UNLIKELY(rsize = sizeof(buf) 40 41 throw TProtocolException(TProtoco

温馨提示

  • 1. 本站所有资源如无特殊说明,都需要本地电脑安装OFFICE2007和PDF阅读器。图纸软件为CAD,CAXA,PROE,UG,SolidWorks等.压缩文件请下载最新的WinRAR软件解压。
  • 2. 本站的文档不包含任何第三方提供的附件图纸等,如果需要附件,请联系上传者。文件的所有权益归上传用户所有。
  • 3. 本站RAR压缩包中若带图纸,网页内容里面会有图纸预览,若没有图纸预览就没有图纸。
  • 4. 未经权益所有人同意不得将文件中的内容挪作商业或盈利用途。
  • 5. 人人文库网仅提供信息存储空间,仅对用户上传内容的表现方式做保护处理,对用户上传分享的文档内容本身不做任何修改或编辑,并不能对任何下载内容负责。
  • 6. 下载文件中如有侵权或不适当内容,请与我们联系,我们立即纠正。
  • 7. 本站不保证下载资源的准确性、安全性和完整性, 同时也不承担用户因使用这些下载资源对自己和他人造成任何形式的伤害或损失。

评论

0/150

提交评论