thrift初识-c++服务端和python客户端
thrift作为一个跨语言的服务部署框架,目前的应用非常广泛。
这里通过thrift实现一个简单的echo服务来加深对其的理解和印象。入门学习thrift强烈推荐官方文档thrift study
整个echo服务分为两个很简单的部分,服务端和客户端,thrift是跨语言的,所以我尝试了两种不同的语言,用C++做服务器端,python写客户端(毕竟简单),在实践之前先介绍下thrift的原理。
thrift工作原理
这里只做下简单介绍,因为我也是初学,网上还是有很多其他的好的博文的。
thrift通过它自定义的一种IDL来定义它RPC的规范(接口和数据类型),然后通过编译器生成不同语言的代码(gen-cpp,gen-java,gen-py等),底层网络传输,协议层的功能都由这些生成的代码来维护。
thrift的network stack有张经典的介绍图如下:
- 图中transport层做为传输,其实是一种网络数据读写的抽象层,把传输和其他系统解耦。
- protocol层,定义transport层的传输的数据格式,作序列化和反序列化的格式要求。
- processor层:A Processor encapsulates the ability to read data from input streams and write to output streams. The input and output streams are represented by Protocol objects.
- server层做一个上述功能特性的聚合。
开发人员一般都在server层写,不过因为业务不同会对不同层次提供的接口采取不同的使用,比如非阻塞的网络IO。
C++服务端
接下来是实践,写了一个简单的echo服务为例。首先在echo.thrift中定义service如下:
service echo{
string echo(1:string msg)
}
然后使用命令thrift -gen cpp echo.thrift和thrift -gen py echo.thrift分别生成c++和python版本的代码,python的用来做客户端。
然后修改生成的cpp目录gen-cpp之下的echo_server.skeleton.cpp,内容如下:
// This autogenerated skeleton file illustrates how to build a server.
// You should copy it to another filename to avoid overwriting it.
#include "echo.h"
#include <protocol/TBinaryProtocol.h>
#include <server/TSimpleServer.h>
#include <transport/TServerSocket.h>
#include <transport/TBufferTransports.h>
#include <iostream>
using namespace ::apache::thrift;
using namespace ::apache::thrift::protocol;
using namespace ::apache::thrift::transport;
using namespace ::apache::thrift::server;
using boost::shared_ptr;
//using namespace ;
class echoHandler : virtual public echoIf {
public:
echoHandler() {
// Your initialization goes here
}
void echo(std::string& _return, const std::string& msg) {
// Your implementation goes here
// printf("echo\n");
std::cout << "get msg: " << msg << std::endl; //主要就是这个函数的修改
_return = msg;
}
};
int main(int argc, char **argv) {
int port = 9090;
shared_ptr<echoHandler> handler(new echoHandler());
shared_ptr<TProcessor> processor(new echoProcessor(handler));
shared_ptr<TServerTransport> serverTransport(new TServerSocket(port));
shared_ptr<TTransportFactory> transportFactory(new TBufferedTransportFactory());
shared_ptr<TProtocolFactory> protocolFactory(new TBinaryProtocolFactory());
TSimpleServer server(processor, serverTransport, transportFactory, protocolFactory);
server.serve();
return 0;
}
之后编译,新手编译可能会遇到两个问题。第一个问题是找不到thrift,g++提示错误Thrift.h: No such file or directory,解决方法也简单,既然是找不到,那就把它添进去,加上g++编译选项 -I /usr/local/include/thrift -L /usr/local/lib,添加thrift的安装路径的头文件库和lib库。 第二个问题是提示error: ‘uint8_t’ does not name a type,解决办法也是加上编译选项-g -DHAVE_NETINET_IN_H,原因参考error:‘uint8_t’ does not name a type. 所以最后编译命令就是 g++ -g -DHAVE_NETINET_IN_H -I /usr/include/thrift -o server *.cpp -lthrift(lib看情况加不加),这样就生成的可执行文件server,server默认监听9090端口。
python客户端
python客户端比较简单,在gen-py目录中添加client.py文件,内容如下:
#!/usr/bin/env python
#coding:utf-8
import sys
sys.path.append('./')
from echo import echo #引入客户端类
from thrift import Thrift
from thrift.transport import TSocket
from thrift.transport import TTransport
from thrift.protocol import TBinaryProtocol
try:
#建立socket
transport = TSocket.TSocket('localhost', 9090)
#选择传输层,这块要和服务端的设置一致
transport = TTransport.TBufferedTransport(transport)
#选择传输协议,这个也要和服务端保持一致,否则无法通信
protocol = TBinaryProtocol.TBinaryProtocol(transport)
#创建客户端
client = echo.Client(protocol)
transport.open()
while 1:
msg = raw_input("input msg: ")
msg_ret = client.echo(msg)
print "echo: "+msg_ret
# print "server - " + msg
#关闭传输
transport.close()
#捕获异常
except Thrift.TException, ex:
print "%s" % (ex.message)
最后运行先运行server,之后执行python client.py能得到这样的结果,在server会收到信息,客户端输入的信息也得到回显。
客户端
input msg: show parameter
echo: show parameter
input msg:
服务端
get msg: show parameter
最后的内容大致如上所示,以后会慢慢写更加复杂的thrift的代码了。