浅谈Thrift内部实现原理
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++编写),它为用户生成了网络通信相关的代码,从而大大减少了用户的编码工作。
浅谈Thrift内部实现原理的更多相关文章
- TODO:浅谈pm2基本工作原理
TODO:浅谈pm2基本工作原理 要谈Node.js pm2的工作原理,需要先来了解撒旦(Satan)和上帝(God)的关系. 撒旦(Satan),主要指<圣经>中的堕天使(也称堕天使撒旦 ...
- 浅谈SpringBoot核心注解原理
SpringBoot核心注解原理 今天跟大家来探讨下SpringBoot的核心注解@SpringBootApplication以及run方法,理解下springBoot为什么不需要XML,达到零配置 ...
- 浅谈springboot自动配置原理
前言 springboot自动配置关键在于@SpringBootApplication注解,启动类之所以作为项目启动的入口,也是因为该注解,下面浅谈下这个注解的作用和实现原理 @SpringBootA ...
- 浅谈 underscore 内部方法 group 的设计原理
前言 真是天一热什么事都不想干,这个月只产出了一篇文章,赶紧写一篇压压惊! 前文(https://github.com/hanzichi/underscore-analysis/issues/15)说 ...
- 浅谈 session 会话的原理
先谈 cookie 网络传输基于的Http协议,是无状态的协议,即每次连接断开后再去连接,服务器是无法判断此次连接的客户端是谁. 如果每次数据传输都需要进行连接和断开,那造成的开销是很巨大的. 为了解 ...
- JAVA NIO之浅谈内存映射文件原理与DirectMemory
JAVA类库中的NIO包相对于IO 包来说有一个新功能是内存映射文件,日常编程中并不是经常用到,但是在处理大文件时是比较理想的提高效率的手段.本文我主要想结合操作系统中(OS)相关方面的知识介绍一下原 ...
- 【NIO】NIO之浅谈内存映射文件原理与DirectMemory
Java类库中的NIO包相对于IO 包来说有一个新功能是内存映射文件,日常编程中并不是经常用到,但是在处理大文件时是比较理想的提高效率的手段.本文我主要想结合操作系统中(OS)相关方面的知识介绍一下原 ...
- 浅谈JavaScript DDOS 攻击原理与防御
前言 DDoS(又名"分布式拒绝服务")攻击历史由来已久,但却被黑客广泛应用.我们可以这样定义典型的DDoS攻击:攻击者指使大量主机向服务器发送数据,直到超出处理能力进而无暇处理正 ...
- 浅谈HashMap 的底层原理
本文整理自漫画:什么是HashMap? -小灰的文章 .已获得作者授权. HashMap 是一个用于存储Key-Value 键值对的集合,每一个键值对也叫做Entry.这些个Entry 分散存储在一个 ...
随机推荐
- chrome备份网站
chrome备份网站 https://www.chromedownloads.net/
- 把握linux内核设计思想(十二):内存管理之slab分配器
[版权声明:尊重原创,转载请保留出处:blog.csdn.net/shallnet,文章仅供学习交流.请勿用于商业用途] 上一节最后说到对于小内存区的请求,假设採用伙伴系统来进行分配,则会在页内产生非 ...
- 【转载】lvs为何不能完全替代DNS轮询
上一篇文章"一分钟了解负载均衡的一切"引起了不少同学的关注,评论中大家争论的比较多的一个技术点是接入层负载均衡技术,部分同学持这样的观点: 1)nginx前端加入lvs和keepa ...
- 创建一个zookeeper的会话(实现watcher)
在先前的章节中,我们利用zkCli去了解了一下主要的zookeeper的操作.在接下来的章节中,我们将会学习一下在应用中是怎样利用zookeeper的api的.接下来我们将利用一个程序展示一下,怎样来 ...
- gcc编译minigui新程序报错
!ggcc *.c -ljpeg -lpng -lminigui -lpthread/usr/local/lib/libminigui.so: undefined reference to `dl ...
- Android经常使用的工具类
主要介绍总结的Android开发中经常使用的工具类,大部分相同适用于Java. 眼下包含HttpUtils.DownloadManagerPro.ShellUtils.PackageUtils. Pr ...
- C++中的getopt的用法
getopt的用法 getopt被用来解析命令行选项参数.就不用自己写东东处理argv了. 点击(此处)折叠或打开 #include <unistd.h> extern char *opt ...
- POJ 1703 Find them, Catch them(种类并查集)
题目链接 这种类型的题目以前见过,今天第一次写,具体过程,还要慢慢理解. #include <cstring> #include <cstdio> #include <s ...
- MapReduce算法形式四:mapjoin
案例四:mapjoin(对个map共同输入,一个reduce) 这个方法主要解决的是,几个表之间的比较,类似于数据库的内外连接,还有一些左右连接之类的,简而言之就是,A表没有的B表有,B表有的A没有或 ...
- 更多的使用自定义元素(CustomElement)。
更多的使用自定义元素(CustomElement).