tars序列化过程:TARS编码协议是一种数据编解码规则,它将整形、枚举值、字符串、序列、字典、自定义结构体等数据类型按照一定的规则编码到二进制数据流中。对端接收到二进制数据流之后,按照相应的规则反序列化可得到原始数值。简单理解,TARS编码协议提供了一种将数据序列化、反序列化的方法。其角色和我们认识的protobuf、json、xml等同。
1.客户端原始请求数据---->序列化---->服务端
借助于tars文件生成的头文件,用代理调用相应的接口,该接口利用tars_invoke(invoke 调用)即可完成序列化传输。
class ConfigAdminProxy : public tars::ServantProxy
{
public:
typedef map TARS_CONTEXT;
tars::Int32 AddConfig(const tars::AddConfigInfo & config,std::string &result,const map &context = TARS_CONTEXT(),map * pResponseContext = NULL)
{
tars::TarsOutputStream _os;
_os.write(config, 1);
_os.write(result, 2);
tars::ResponsePacket rep;
std::map _mStatus;
tars_invoke(tars::TARSNORMAL,"AddConfig", _os.getByteBuffer(), context, _mStatus, rep);
if(pResponseContext)
{
*pResponseContext = rep.context;
}
tars::TarsInputStream _is;
_is.setBuffer(rep.sBuffer);
tars::Int32 _ret;
_is.read(_ret, 0, true);
_is.read(result, 2, true);
return _ret;
} //只显示了一个类成员函数
_objectProxy->getProxyProtocol().requestFunc(msg->request, msg->sReqData);
其中msg中包含成员 RequestPacket request(请求消息体)和ResponsePacket response(响应消息体)。
这里的resquestFunc来自ProxyProtocol::tarsResponse, 即如下所示:
_proxyProtocol.requestFunc = ProxyProtocol::tarsRequest;
void ProxyProtocol::tarsRequest(const RequestPacket& request, string& buff)
{
TarsOutputStream os;
request.writeTo(os);
tars::Int32 iHeaderLen = htonl(sizeof(tars::Int32) + os.getLength());
buff.clear();
buff.reserve(sizeof(tars::Int32) + os.getLength());
buff.append((const char*)&iHeaderLen, sizeof(tars::Int32));
buff.append(os.getBuffer(), os.getLength());
}
_objectProxy->getProxyProtocol().requestFunc(msg->request, msg->sReqData);
std::unique_ptr_trans //客户端利用Transceiver类收发解析请求 。
_trans->sendRequest(msg->sReqData.c_str(),msg->sReqData.size())
2、 服务端---->反序列化---->原始请求数据
int onDispatch(tars::TarsCurrentPtr _current, vector &_sResponseBuffer)
{
static ::std::string __tars__ConfigAdmin_all[]=
{
"AddConfig",
.......
};
pair r = equal_range(__tars__ConfigAdmin_all, __tars__ConfigAdmin_all+14, _current->getFuncName());
if(r.first == r.second) return tars::TARSSERVERNOFUNCERR;
switch(r.first - __tars__ConfigAdmin_all)
{
case 0:
{
tars::TarsInputStream _is;
_is.setBuffer(_current->getRequestBuffer());
tars::AddConfigInfo config;
std::string result;
if (_current->getRequestVersion() == TUPVERSION)
{
UniAttribute tarsAttr;
tarsAttr.setVersion(_current->getRequestVersion());
tarsAttr.decode(_current->getRequestBuffer());
tarsAttr.get("config", config);
tarsAttr.getByDefault("result", result, result);
}
else
{
_is.read(config, 1, true);
_is.read(result, 2, false);
}
tars::Int32 _ret = AddConfig(config,result, _current);
if(_current->isResponse())
{
if (_current->getRequestVersion() == TUPVERSION )
{
UniAttribute tarsAttr;
tarsAttr.setVersion(_current->getRequestVersion());
tarsAttr.put("", _ret);
tarsAttr.put("result", result);
tarsAttr.encode(_sResponseBuffer);
}
else
{
tars::TarsOutputStream _os;
_os.write(_ret, 0);
_os.write(result, 2);
_os.swap(_sResponseBuffer);
}
}
return tars::TARSSERVERSUCCESS;
}
服务端利用onDispatch接口处理收到的二进制请求流,从_current中得到请求数据,然后把本地的调用结果放到vector & _sResponseBuffer中返回。
3、 服务端原始返回数据---->序列化----->客户端
RequestF.h 中定义了ResponsePacket结构体。
//位置:cpp/servant/libservant/TarsCurrent.cpp 221
void TarsCurrent::sendResponse(int iRet, const vector& buffer, const map& status, const string & sResultDesc)
{
//省略部分代码
………………
TarsOutputStream os;
if (_request.iVersion != TUPVERSION)
{
//将数据放到ResponsePacket结构中
ResponsePacket response;
response.iRequestId = _request.iRequestId;
response.iMessageType = _request.iMessageType;
response.cPacketType = TARSNORMAL;
response.iVersion = TARSVERSION;
response.status = status;
response.sBuffer = buffer;
response.sResultDesc = sResultDesc;
response.context = _responseContext;
response.iRet = iRet;
TLOGINFO("[TARS]TarsCurrent::sendResponse :"
<< response.iMessageType << "|"
<< _request.sServantName << "|"
<< _request.sFuncName << "|"
<< response.iRequestId << endl);
//调用序列化方法,response中的数据都保存在了os中,调用了tars.h中的write接口
response.writeTo(os);
}
//省略部分代码
…………………………
//获取内容长度
tars::Int32 iHeaderLen = htonl(sizeof(tars::Int32) + os.getLength());
string s = "";
//返回的s的格式是内容长度+内容
s.append((const char*)&iHeaderLen, sizeof(tars::Int32));
s.append(os.getBuffer(), os.getLength());
_servantHandle->sendResponse(_uid, s, _ip, _port, _fd);
}
4.客户端----->反序列化----->原始返回数据
//位置:cpp/servant/libservant/Transceiver.cpp 331
int TcpTransceiver::doResponse(list& done)
{
…………
if(!_recvBuffer.IsEmpty())
{
try
{
//接收到的服务端的序列化好的数据
const char* data = _recvBuffer.ReadAddr();
size_t len = _recvBuffer.ReadableSize();
size_t pos = 0;
//获取协议封装类
ProxyProtocol& proto = _adapterProxy->getObjProxy()->getProxyProtocol();
if (proto.responseExFunc)
{
long id = _adapterProxy->getId();
//将data反序列化到done中
pos = proto.responseExFunc(data, len, done, (void*)id);
}
…………
}
}
这里的responseExFunc来自ProxyProtocol::tarsRespons(cpp/servant/AppProtocal.h 398)
template
static size_t tarsResponseLen(const char* recvBuffer, size_t length, list& done)
{
…………
TarsInputStream is;
//将数据放入is中
is.setBuffer(recvBuffer + pos + sizeof(tars::Int32), iHeaderLen - sizeof(tars::Int32));
pos += iHeaderLen;
//将is中的数据进行反序列化,填充到rsp中
ResponsePacket rsp;
rsp.readFrom(is);
…………//在tars生成的代理接口函数中对rsp解析RPC调用的返回值。
}
从上面代码中可以看出:
序列化数据使用的是:ResponsePacket.writeTo()
反序列化数据使用的是:ResponsePacket.readFrom()
把结构化数据序列化,用大白话解释就是想办法把不同类型的数据按照顺序放在一个字符串里。反序列化就是还能从这个字符串里把类型和数据正确解析出来。一般来说,要达成正确的效果,有三个因素是必须考虑的:
Tars协议也跳不出这个基本规则,它的数据是由两部分组成:
| HEAD | BUF |
| TAG1(4 bits) | TYPE(4 bits) | TAG2(1 byte或者8 bits)
//位置:/cpp/servant/tup/Tars.h 60行
//数据头类型
#define TarsHeadeChar 0
#define TarsHeadeShort 1
#define TarsHeadeInt32 2
#define TarsHeadeInt64 3
#define TarsHeadeFloat 4
#define TarsHeadeDouble 5
#define TarsHeadeString1 6
#define TarsHeadeString4 7
#define TarsHeadeMap 8
#define TarsHeadeList 9
#define TarsHeadeStructBegin 10
#define TarsHeadeStructEnd 11
#define TarsHeadeZeroTag 12
#define TarsHeadeSimpleList 13