http://dongxicheng.org/tag/thrift/

http://dongxicheng.org/search-engine/thrift-internals/

Thrift由两部分组成:编译器(在compiler目录下,采用C++编写)和服务器(在lib目录下),其中编译器的作用是将用户定义的thrift文件编译生成对应语言的代码,而服务器是事先已经实现好的、可供用户直接使用的RPC Server(当然,用户也很容易编写自己的server)。同大部分编译器一样,Thrift编译器(采用C++语言编写)也分为词法分析、语法分析等步骤,Thrift使用了开源的flex和Bison进行词法语法分析(具体见thrift.ll和thrift.yy),经过语法分析后,Thrift根据对应语言的模板(在compiler\cpp\src\generate目录下)生成相应的代码。对于服务器实现而言,Thrift仅包含比较经典的服务器模型,比如单线程模型(TSimpleServer),线程池模型(TThreadPoolServer)、一个请求一个线程(TThreadedServer)和非阻塞模型(TNonblockingServer)等。本文将以C++为例进行一个实例分析。

假设用户编写了以下Thrift文件:

 struct LogInfo {
: required string name,
: optional string content,
}
service LogSender {
void SendLog(:list<LogInfo> loglist);
}

用户使用命令“thrift –gen cpp example.thrift”可生成C++代码,该代码包含以下文件:

example_constants.h
example_constants.cpp
example_types.h //struct定义
example_types.cpp //struct实现
LogSender.h //service定义
LogSender.cpp //service实现和LogSenderClient实现
LogSender_server.skeleton.cpp //一个实例RPC Server

用户可以这样编写Client:

 shared_ptr socket(new TSocket(“8.8.8.8″, ));
shared_ptr transport(new TBufferedTransport(socket));
shared_ptr protocol(new TBinaryProtocol(transport));
LogSenderClient client(protocol);
try {
transport->open();
vector<LogInfo> logInfos;
LogInfo logInfo(“image”, “:: visit:xxxxxx”);
logInfos.push_back(logInfo);
…..
client.SendLog(logInfos);
transport->close();
} catch (TException &tx) {
printf(“ERROR: %s\n”, tx.what());
}

为了深入分析这段代码,我们看一下client.SendLog()函数的内部实现(在LogSender.cpp中):

 void LogSenderClient::SendLog(const std::vector<LogInfo> & loglist)
{
send_SendLog(loglist);
recv_SendLog();
}
void LogSenderClient::send_SendLog(const std::vector<LogInfo> & loglist)
{
int32_t cseqid = ;
oprot_->writeMessageBegin(“SendLog”, ::apache::thrift::protocol::T_CALL, cseqid);
LogSender_SendLog_pargs args;
args.loglist = &loglist;
args.write(oprot_);
oprot_->writeMessageEnd();
oprot_->getTransport()->flush();
oprot_->getTransport()->writeEnd();
}
void LogSenderClient::recv_SendLog()
{
int32_t rseqid = ;
std::string fname;
::apache::thrift::protocol::TMessageType mtype;
iprot_->readMessageBegin(fname, mtype, rseqid);
if (mtype == ::apache::thrift::protocol::T_EXCEPTION) {
…..
}
if (mtype != ::apache::thrift::protocol::T_REPLY) {
……
}
if (fname.compare(“SendLog”) != ) {
……
}
LogSender_SendLog_presult result;
result.read(iprot_);
iprot_->readMessageEnd();
iprot_->getTransport()->readEnd();
return;
}

阅读上面的代码,可以看出,RPC函数SendLog()实际上被转化成了两个函数:send_SendLog和recv_SendLog,分别用于发送数据和接收结果。数据是以消息的形式表示的,消息头部是RPC函数名,消息内容是RPC函数的参数。

我们再进一步分析RPC Server端,一个server的编写方法(在LogSender.cpp中)如下:

 shared_ptr protocolFactory(new TBinaryProtocolFactory());
shared_ptr handler(new LogSenderHandler());
shared_ptr processor(new LogSenderProcessor(handler));
shared_ptr serverTransport(new TServerSocket());
shared_ptr transportFactory(new TBufferedTransportFactory());
TSimpleServer server(processor,
serverTransport,
transportFactory,
protocolFactory);
printf(“Starting the server…\n”);
server.serve();

Server端最重要的类是LogSenderProcessor,它内部有一个映射关系processMap_,保存了所有RPC函数名到函数实现句柄的映射,对于LogSender而言,它只保存了一个RPC映射关系:

processMap_[" SendLog"] = &LogSenderProcessor::process_SendLog;

其中,process_SendLog是一个函数指针,它的实现如下:

 void LogSenderProcessor::process_SendLog(int32_t seqid, ::apache::thrift::protocol::TProtocol* iprot, ::apache::thrift::protocol::TProtocol* oprot)
{
LogSender_SendLog_args args;
args.read(iprot);
iprot->readMessageEnd();
iprot->getTransport()->readEnd();
LogSender_SendLog_result result;
try {
iface_->SendLog(args.loglist);//调用用户编写的函数
} catch (const std::exception& e) {
……
}
oprot->writeMessageBegin(“SendLog”, ::apache::thrift::protocol::T_REPLY, seqid);
result.write(oprot);
oprot->writeMessageEnd();
oprot->getTransport()->flush();
oprot->getTransport()->writeEnd();
}

LogSenderProcessor中一个最重要的函数是process(),它是服务器的主体函数,服务器端(socket server)监听到客户端有请求到达后,会检查消息类型,并检查processMap_映射,找到对应的消息处理函数,并调用之(注意,这个地方可以采用各种并发模型,比如one-request-one-thread,thread pool等)。

通过上面的分析可以看出,Thrift最重要的组件是编译器(采用C++编写),它为用户生成了网络通信相关的代码,从而大大减少了用户的编码工作。

最新文章

  1. mysql数据库主从同步
  2. OpenGL入门
  3. CloudSim介绍和使用
  4. UIScrollView无法滚动的解决办法及UIScrollView的代理(delegate)
  5. 动态规划VS分治策略
  6. 数据库 PSU,SPU(CPU),Bundle Patches 和 Patchsets 补丁号码快速参考 (文档 ID 1922396.1)
  7. 透过IL看C#:switch语句(转)
  8. python下的orm基本操作(1)--Mysql下的CRUD简单操作(含源码DEMO)
  9. jsp页面 列表 展示 ajax异步实现
  10. 【应用笔记】【AN002】通过iTool2基于MinGW平台读写EEPROM
  11. nautilus-open-terminal很有用的插件--鼠标右键打开终端
  12. Android -- PowerManager和PowerManager.WakeLock
  13. WinAPI: FindWindow、FindWindowEx - 查找窗口
  14. SQL的表连接
  15. otl库(以前不知道有这个库,并且还可以在Unix下使用)
  16. 写一个背景渐变的TextView输入框
  17. java 图片质量压缩
  18. PCI9054芯片的型号说明及购买建议
  19. Solution for link error:Cannot Open File &#39;python27_d.lib&#39;
  20. php json 中文不转义 &amp; 转义为中文

热门文章

  1. BUPT复试专题—内存分配(2014-2)
  2. [转]PHP并发IO编程之路(深度长文)
  3. linux 环境 php 链接 sqlserver 2008
  4. invlpg 指令简单介绍
  5. bsp开发之驱动开发
  6. OpenStack Live Migration
  7. RecyclerView(替代ListView)使用方法介绍
  8. Spark学习笔记:(一)入门 glance
  9. JAVA运行时异常及常见的5中RuntimeExecption
  10. 使用Apache Ant合并多个jar