thrift网络通讯架构解析与实例验证
来源:互联网 发布:php 去除双引号 编辑:程序博客网 时间:2024/06/04 23:27
Thrift实现了C/S模式,通过代码生成工具将接口定义文件生成服务器端和客户端代码(可以为不同语言),从而实现服务端和客户端跨语言的支持。用户在Thirft描述文件中声明自己的服务,这些服务经过编译后会生成相应语言的代码文件,然后用户实现服务(客户端调用服务,服务器端提服务)便可以了。
一、Thrift Rpc整体架构
Thrift的协议栈如图:
在Client和Server的最顶层都是用户自定义的处理逻辑,也就是说用户只需要编写用户逻辑,就可以完成整套的RPC调用流程。用户逻辑的下一层是Thrift自动生成的代码,这些代码主要用于结构化数据的解析,发送和接收,同时服务器端的自动生成代码中还包含了RPC请求的转发(Client的A调用转发到Server A函数进行处理)
● 底层IO模块,负责实际的数据传输,包括Socket,文件,或者压缩数据流等。
● TTransport负责以字节流方式发送和接收Message,是底层IO模块在Thrift框架中的实现,每一个底层IO模块都会有一个对应TTransport来负责Thrift的字节流(Byte Stream)数据在该IO模块上的传输。例如TSocket对应Socket传输,TFileTransport对应文件传输。
● TProtocol主要负责结构化数据组装成Message,或者从Message结构中读出结构化数据。TProtocol将一个有类型的数据转化为字节流以交给TTransport进行传输,或者从TTransport中读取一定长度的字节数据转化为特定类型的数据。如int32会被TBinaryProtocol Encode为一个四字节的字节数据,或者TBinaryProtocol从TTransport中取出四个字节的数据Decode为int32。
● TServer负责接收Client的请求,并将请求转发到Processor进行处理。TServer主要任务就是高效的接受Client的请求,特别是在高并发请求的情况下快速完成请求。
● Processor(或者TProcessor)负责对Client的请求做出相应,包括RPC请求转发,调用参数解析和用户逻辑调用,返回值写回等处理步骤。Processor是服务器端从Thrift框架转入用户逻辑的关键流程。Processor同时也负责向Message结构中写入数据或者读出数据。
二、数据传输方式(TTransport)
- TSocket : 阻塞式socker;
- THttpTransport : 采用HTTP传输协议进行数据传输;
- TFramedTransport : 以frame为单位进行传输,非阻塞式服务中使用;
- TFileTransport : 以文件形式进行传输;
- TMemoryTransport : 将内存用于I/O. java实现时内部实际使用了简单的ByteArrayOutputStream;
- TZlibTransport : 使用zlib进行压缩, 与其他传输方式联合使用。当前无java实现;
- TBufferedTransport : 对某个transport对象操作的数据进行buffer,即从buffer中读取数据进行传输,或将数据直接写入到buffer
open Opens the tranpsortclose Closes the tranportisOpen Indicates whether the transport is openread Reads from the transportwrite Writes to the transportflush Forces any pending writes除以上的TTransport接口外,还有一个TServerTransport接口,用来接受或创建原始传输对象。它的接口如下:
open Opens the transportlisten Begins listening for connectionsaccept Returns a new client transportclose Closes the transport
三、传输协议(TProtocol)
thrift做到很好的让用户在服务器端与客户端选择对应的传输协议,总体上一般为2种传输协议:二进制或者文本,如果想要节省带宽可以采用二进制的协议,如果希望方便抓包、调试则可以选择文本协议,用户可用根据自己的项目需求选择对应的协议。
- TCompactProtocol : 紧凑的、高效的二进制传输协议;
- TBinaryProtocol : 基于二进制传输的协议,使用方法与TCompactProtocol 相同
- TJSONProtocol : 使用json格式编码传输协议
- TDebugProtocol : 使用易懂的可读的文本格式,以便于debug
writeMessageBegin(name, type, seq)writeMessageEnd()writeStructBegin(name)writeStructEnd()writeFieldBegin(name, type, id)writeFieldEnd()writeFieldStop()writeMapBegin(ktype, vtype, size)writeMapEnd()writeListBegin(etype, size)writeListEnd()writeSetBegin(etype, size)writeSetEnd()writeBool(bool)writeByte(byte)writeI16(i16)writeI32(i32)writeI64(i64)writeDouble(double)writeString(string)name, type, seq = readMessageBegin()readMessageEnd()name = readStructBegin()readStructEnd()name, type, id = readFieldBegin()readFieldEnd()k, v, size = readMapBegin()readMapEnd()etype, size = readListBegin()readListEnd()etype, size = readSetBegin()readSetEnd()bool = readBool()byte = readByte()i16 = readI16()i32 = readI32()i64 = readI64()double = readDouble()string = readString()注意到每个write函数有且仅有一个相应的read方法。WriteFieldStop()异常是一个特殊的方法,标志一个结构的结束。读一个结构的过程是readFieldBegin()直到遇到stop域,然后readStructEnd()。生成的代码依靠这个调用顺序,来确保一个协议编码器所写的每一件事,都可被一个相应的协议解码器读取。 这组功能在设计上更加注重健壮性,而非必要性。例如,writeStructEnd()不是严格必需的,因为一个结构体的结束可用stop域表示。
四、服务器网络模型(TServer)
- TSimpleServer : 简单的单线程网络模型, 同时只能服务一个client,通常是结合TSocket用于测试;
- TThreadedServer : 多线程网络模型,使用阻塞式IO,为每个请求创建一个线程;
- TThreadPoolServer : 线程池网络模型,使用阻塞式IO,将每个请求都加入到线程池中;
- TNonblockingServer : 多线程服务模型,使用非阻塞式IO(需使用TFramedTransport数据传输方式);
TServer对象通常如下工作:
1)使用TServerTransport获得一个TTransport2)使用TTransportFactory,可选地将原始传输转换为一个适合的应用传输(典型的是使用TBufferedTransportFactory)3)使用TProtocolFactory,为TTransport创建一个输入和输出4)调用TProcessor对象的process()方法Thrift中定义一个server的方法如下:
TSimpleServer server( boost::make_shared<CalculatorProcessor>(boost::make_shared<CalculatorHandler>()), boost::make_shared<TServerSocket>(9090), boost::make_shared<TBufferedTransportFactory>(), boost::make_shared<TBinaryProtocolFactory>()); TThreadedServer server( boost::make_shared<CalculatorProcessorFactory>(boost::make_shared<CalculatorCloneFactory>()), boost::make_shared<TServerSocket>(9090), //port boost::make_shared<TBufferedTransportFactory>(), boost::make_shared<TBinaryProtocolFactory>()); const int workerCount = 4;//线程池容量 boost::shared_ptr<ThreadManager> threadManager = ThreadManager::newSimpleThreadManager(workerCount); threadManager->threadFactory(boost::make_shared<PlatformThreadFactory>()); threadManager->start(); TThreadPoolServer server( boost::make_shared<CalculatorProcessorFactory>(boost::make_shared<CalculatorCloneFactory>()), boost::make_shared<TServerSocket>(9090), boost::make_shared<TBufferedTransportFactory>(), boost::make_shared<TBinaryProtocolFactory>(), threadManager); TNonBlockingServer server( boost::make_shared<CalculatorProcessorFactory>(boost::make_shared<CalculatorCloneFactory>()), boost::make_shared<TServerSocket>(9090), boost::make_shared<TFramedTransportFactory>(), boost::make_shared<TBinaryProtocolFactory>(), threadManager); server.serve();//启动server
五、TProcessor/Processor
Thrift在生成Processor的时候,会遵守一些命名规则,可以参考 Thrift Generator部分的介绍。
TProcessor对于一次RPC调用的处理过程可以概括为:
1、TServer接收到RPC请求之后,调用TProcessor.process进行处理
2、TProcessor.process首先调用TTransport.readMessageBegin接口,读出RPC调用的名称和RPC调用类型。如果RPC调用类型是RPC Call,则调用TProcessor.process_fn继续处理,对于未知的RPC调用类型,则抛出异常。
3、TProcessor.process_fn根据RPC调用名称到自己的processMap中查找对应的RPC处理函数。如果存在对应的RPC处理函数,则调用该处理函数继续进行请求响应。不存在则抛出异常。
a)在这一步调用的处理函数,并不是最终的用户逻辑。而是对用户逻辑的一个包装。
b)processMap是一个标准的std::map。Key为RPC名称。Value是对应的RPC处理函数的函数指针。 processMap的初始化是在Processor初始化的时候进行的。Thrift虽然没有提供对processMap做修改的API,但是仍可以通过继承TProcessor来实现运行时对processMap进行修改,以达到打开或关闭某些RPC调用的目的。
4、RPC处理函数是RPC请求处理的最后一个步骤,它主要完成以下三个步骤:
a) 调用RPC请求参数的解析类,从TProtocol中读入数据完成参数解析。不管RPC调用的参数有多少个,Thrift都会将参数放到一个Struct中去。Thrift会检查读出参数的字段ID和字段类型是否与要求的参数匹配。对于不符合要求的参数都会跳过。这样,RPC接口发生变化之后,旧的处理函数在不做修改的情况,可以通过跳过不认识的参数,来继续提供服务。进而在RPC框架中提供了接口的多Version支持。
b) 参数解析完成之后,调用用户逻辑,完成真正的请求响应。
c) 用户逻辑的返回值使用返回值打包类进行打包,写入TProtocol。
六、ThriftClient
ThriftClient跟TProcessor一样都主要操作InputProtocol和OutputProtocol,不同的是ThritClient将RPC调用分为Send和receive两个步骤。1、Send步骤,将用户的调用参数作为一个整体的Struct写入TProcotol,并发送到TServer。
2、Send结束之后,ThriftClient便立刻进入Receive状态等待TServer的相应。对于TServer返回的响应,使用返回值解析类进行返回值解析,完成RPC调用。
struct Student{ 1: i32 sno, 2: string sname, 3: bool ssex, 4: i16 sage,}service Serv{ i32 put(1: Student s),}
thrift -r --gen cpp student.thrift在目录下生成了gen-cpp文件夹,内有文件 Serv.cpp Serv.h Serv_server.skeleton.cpp student_constants.cpp student_constants.h student_types.cpp student_types.h,
#include <concurrency/ThreadManager.h> //zml#include <concurrency/PosixThreadFactory.h> //zml#include "gen-cpp/Serv.h"#include <protocol/TBinaryProtocol.h>#include <server/TSimpleServer.h>#include <transport/TServerSocket.h>#include <transport/TBufferTransports.h>#include <server/TNonblockingServer.h> //zml using namespace ::apache::thrift;using namespace ::apache::thrift::protocol;using namespace ::apache::thrift::transport;using namespace ::apache::thrift::server; using namespace::apache::thrift::concurrency; //zml using boost::shared_ptr; #define THREAD_NUM 2const int g_port = 9090;class ServHandler : virtual public ServIf { public: ServHandler() { // Your initialization goes here } int32_t put(const Student& s) { // Your implementation goes here printf("put student.sno=%d\n", s.sno); return s.sno; }};int thrift_server_run(){ //创建thrift server shared_ptr<ServHandler> handler(new ServHandler()); shared_ptr<TProcessor> processor(new ServProcessor(handler)); shared_ptr<TProtocolFactory> protocolFactory(new TBinaryProtocolFactory()); shared_ptr<ThreadManager> threadManager = ThreadManager::newSimpleThreadManager(THREAD_NUM); shared_ptr<PosixThreadFactory> threadFactory = shared_ptr<PosixThreadFactory> (new PosixThreadFactory()); //PosixThreadFactory可以自定义(继承于ThreadFactory) threadManager->threadFactory(threadFactory); threadManager->start(); TNonblockingServer server(processor, protocolFactory, g_port, threadManager); try { server.serve(); } catch(TException e) { printf("Server.serve() failed\n"); exit(-1); } return 0;}int main(int argc, char **argv) { thrift_server_run(); while(1) { sleep(10); } return 0;}
4、编写服务端代码client.cpp
#include "gen-cpp/Serv.h" // 替换成你的.h #include <transport/TSocket.h>#include <transport/TBufferTransports.h>#include <protocol/TBinaryProtocol.h>using namespace apache::thrift;using namespace apache::thrift::protocol;using namespace apache::thrift::transport;using boost::shared_ptr;int main(){boost::shared_ptr<TSocket> socket(new TSocket("localhost", 9090));//对接nonblockingServer时必须的,对普通server端时用boost::shared_ptr<TTransport> transport(new TBufferedTransport(socket));boost::shared_ptr<TTransport> transport(new TFramedTransport(socket)); boost::shared_ptr<TProtocol> protocol(new TBinaryProtocol(transport));ServClient client(protocol);//设置发送、接收、连接超时socket->setConnTimeout(2000);socket->setRecvTimeout(2000);socket->setSendTimeout(2000);transport->open();//insert your code here Student stu;stu.sno = 1;stu.sname = "zml";stu.ssex = 0;stu.sage = 25;int ret = client.put(stu);printf("client put ret=%d\n", ret);transport->close();return 0;}
5、编译执行
g++ -g -DHAVE_NETINET_IN_H -I. -I/usr/local/include/thrift -L/usr/local/lib gen-cpp/Serv.cpp gen-cpp/student_types.cpp gen-cpp/student_constants.cpp server.cpp -o server -lthriftnb -levent -lthrift -lrt
lijinqi@ubuntu:~/test/thrift$ ./serverThrift: Thu Sep 21 16:31:13 2017 TNonblockingServer: Serving on port 9090, 1 io threads.Thrift: Thu Sep 21 16:31:13 2017 TNonblockingServer: using libevent 2.0.21-stable method epollThrift: Thu Sep 21 16:31:13 2017 TNonblocking: IO thread #0 registered for listen.Thrift: Thu Sep 21 16:31:13 2017 TNonblocking: IO thread #0 registered for notify.Thrift: Thu Sep 21 16:31:13 2017 TNonblockingServer: IO thread #0 entering loop...put student.sno=1
lijinqi@ubuntu:~/test/thrift$ ./client client put ret=1
- thrift网络通讯架构解析与实例验证
- Thrift入门与实例
- Thrift编译与验证 - python
- Thrift 原理与使用实例
- Thrift 原理与使用实例
- apache thrift介绍与实例
- Thrift实例
- thrift 实例
- thrift实例
- 网络通讯服务器的架构选择
- Thrift 在Windows下的安装与实例 vs2010
- 解析与架构
- 《VC网络通讯编程实例》目录
- Thrift源码系列----4.数据的解析与发送、接收
- Thrift 架构设计
- Thrift架构简介
- ajax实例与解析
- Thrift 实例 Helloworld
- Quick tutorial to configure Oracle Restart without ASM
- 基于GPU加速的二维VTI介质一阶拟声波方程有限差分地震正演模拟
- Oracle OGG
- 我的深度学习之helloworld小程序
- 自我救赎之路--Java(Intellij IDEA)
- thrift网络通讯架构解析与实例验证
- 如何在Android源码编译系统中添加一个C项目
- Selenium Webdriver简介
- UE4 代理事件(C++)
- 面向对象 oop Class和Object
- Java String 转换方法
- android测量View的宽高
- WOJ1274-Sum of Different Primes
- Socket详解