1. Thrift框架理解
对于Thrift框架的理解,我们使用官方提供的框架图来讲解一下
-
client/server
对于client来说,该层是比较简单的client的业务逻辑代码,而对于server来说,有提供的几种模式的server。
-
Thrift的Protocol层
对于RPC来说需要能正确的传输调用的信息已经返回的结果,那么protocol层则主要负责序列化和反序列化,该层主要有以下几种,对于Binary来说,那么使用的是Binary的方式来进行序列化,那么会涉及到一定的协议格式,对于JSON来说,使用的则是以JSON的方式序列化。
-
Thrift的Transport层
由于RPC是一种特殊的网络编程,那么需要封装一层传输层来支持底层的网络通信,而Transport则负责这层。Transport层不仅仅用来支持底层的网络通信,它可能还会对Protocol层的数据进一步分装。比如针对Framed类型来说,该层还会多4个字节的字段用来存储Protocol层的字节串长度。
那么针对采用TCP/IP作为更底层的通信协议的话,整个通信过程如下图所示
2. Thrift类概述
该图引用自CSDN_Thrift源码解析(一)主要类概述。该图解析的thrift源码是基于Java语言的1.0.0版本,虽然本系列接下去是基于C++在那进行讲解的,但是也具有一定的参考性。
根据上图大概可以分为以下几个类
- TTransport:客户端传输层相关的类;
- TServerTransport:服务端传输层相关的类;
- TProtocol:序列化、反序列化相关的类;
- TServer:服务器的IO事件流模型相关的类;
- TProcessor:函数,接口调用相关的类;
下面主要针对TTransport和Tprotocol层的代码进行阐述。
3. Protocol层的源码
上面提到Protocol层主要是负责序列化的,Thrift的序列化协议主要包含以下几种,
- TBinaryProtocol
- TCompactProtocol
- TJSONProtocol
- …
以上这些协议都继承自TProtocol这个抽象类,那么我们先来看一下TProtocol这个类大致的内容,比较详细的可以直接去看c++源代码,该类中主要是一些读写函数的抽象,write主要负责序列化,read主要负责反序列化。
class TProtocol {
public:
virtual ~TProtocol();
/**
* Writing functions.
*/
virtual uint32_t writeMessageBegin_virt(const std::string& name,
const TMessageType messageType,
const int32_t seqid) = 0;
virtual uint32_t writeMessageEnd_virt() = 0;
virtual uint32_t writeStructBegin_virt(const char* name) = 0;
virtual uint32_t writeStructEnd_virt() = 0;
virtual uint32_t writeFieldBegin_virt(const char* name,
const TType fieldType,
const int16_t fieldId) = 0;
virtual uint32_t writeFieldEnd_virt() = 0;
virtual uint32_t writeFieldStop_virt() = 0;
......
virtual uint32_t writeBool_virt(const bool value) = 0;
virtual uint32_t writeByte_virt(const int8_t byte) = 0;
virtual uint32_t writeI16_virt(const int16_t i16) = 0;
virtual uint32_t writeI32_virt(const int32_t i32) = 0;
virtual uint32_t writeI64_virt(const int64_t i64) = 0;
virtual uint32_t writeDouble_virt(const double dub) = 0;
virtual uint32_t writeString_virt(const std::string& str) = 0;
virtual uint32_t writeBinary_virt(const std::string& str) = 0;
/**
* Reading functions
*/
virtual uint32_t readMessageBegin_virt(std::string& name,
TMessageType& messageType,
int32_t& seqid) = 0;
virtual uint32_t readMessageEnd_virt() = 0;
virtual uint32_t readStructBegin_virt(std::string& name) = 0;
virtual uint32_t readStructEnd_virt() = 0;
virtual uint32_t readFieldBegin_virt(std::string& name, TType& fieldType, int16_t& fieldId) = 0;
......
}
下面我们重点看一下TBinaryProtocol的具体实现,这边就挑几个主要的进行阐述。
template <class Transport_, class ByteOrder_>
uint32_t TBinaryProtocolT<Transport_, ByteOrder_>::writeMessageBegin(const std::string& name,
const TMessageType messageType,
const int32_t seqid) {
if (this->strict_write_) {
int32_t version = (VERSION_1) | ((int32_t)messageType);
uint32_t wsize = 0;
wsize += writeI32(version);
wsize += writeString(name);
wsize += writeI32(seqid);
return wsize;
} else {
uint32_t wsize = 0;
wsize += writeString(name);
wsize += writeByte((int8_t)messageType);
wsize += writeI32(seqid);
return wsize;
}
}
template <class Transport_, class ByteOrder_>
uint32_t TBinaryProtocolT<Transport_, ByteOrder_>::writeFieldStop() {
return writeByte((int8_t)T_STOP);
}
template <class Transport_, class ByteOrder_>
uint32_t TBinaryProtocolT<Transport_, ByteOrder_>::writeI32(const int32_t i32) {
int32_t net = (int32_t)ByteOrder_::toWire32(i32);
this->trans_->write((uint8_t*)&net, 4);
return 4;
}
template <class Transport_, class ByteOrder_>
template <typename StrType>
uint32_t TBinaryProtocolT<Transport_, ByteOrder_>::writeString(const StrType& str) {
if (str.size() > static_cast<size_t>((std::numeric_limits<int32_t>::max)()))
throw TProtocolException(TProtocolException::SIZE_LIMIT);
uint32_t size = static_cast<uint32_t>(str.size());
uint32_t result = writeI32((int32_t)size);
if (size > 0) {
this->trans_->write((uint8_t*)str.data(), size);
}
return result + size;
}
template <class Transport_, class ByteOrder_>
uint32_t TBinaryProtocolT<Transport_, ByteOrder_>::writeBinary(const std::string& str) {
return TBinaryProtocolT<Transport_, ByteOrder_>::writeString(str);
}
writeMessageBegin
:序列化message的开始部分,可以理解为message的头部,thrift中的message表示一次接口调用、接口调用结果或者异常。这个函数中主要是调用相应的write函数来序列和写入thrift的版本、message的name以及seqid等基本信息,比如针对一个函数调用而言,那么name字段就是函数的名字。
writeFieldStop
:所有字段序列化完成之后,调用writeByte
函数写入一个T_STOP标识符,表示结束。
writeI32
:序列化int类型的数据,调用protocol层封装的transport类的实例来写入一个int类型。
writeString
:序列化string类型的数据,写入字符串的内容和4字节的字符串大小。
writeBinary
:在C++中,binary类型还是按照string类型来写入。
相关的read操作就是上述write操作的逆操作者,想要了解的可以直接去看源码。