Thrift是facebook的一個技術核心框架,07年四月開放源碼,08年5月進入apache孵化器。java
簡言之,開發者能夠經過寫一個.thrift文件,定義相應的數據結構和服務接口,該thrift文件會由Thrift相應的解釋器解釋生成指定的類型(C++,java等等)代碼,而後用戶在客戶端和服務器端,分別在生成的代碼裏編寫相應的服務接口函數,並作相應配置選擇,就能夠實現跨平臺的rpc調用。apache
這裏給出一個使用的簡單例子,之中牽扯到了一些編譯方面的細節問題。服務器
定義數據結構的.thrift文件book.thrift:數據結構
//book.thrift namespace cpp example struct Book_Info { 1: i32 book_id, 2: string book_name, 3: string book_author, 4: double book_price, 5: string book_publisher, }
定義rpc服務接口的rpc.thrift文件,定義了service類:架構
//rpc.thrift namespace cpp example include "book.thrift" service BookServlet { bool Sender(1: list<book.Book_Info> books); oneway void Sender2(1: list<book.Book_Info> books); }
調用thrift的編譯器,將上面的兩個thrift文件轉化爲對應語言的代碼,這裏我選了C++:框架
thrift --gen cpp book.thrift
thrift --gen cpp rpc.thrift
而後會在當前目錄下生成一個./gen-cpp文件夾,該文件夾裏就是生成的相應的cpp文件,所生成的文件有如下幾類[1]:socket
對應的,對於本例,所生成的文件列表以下: 函數
其中的makefile不是thrift生成的文件,是我編譯用的,主要關注其餘文件。這個gen-cpp的文件客戶端和服務器端都須要。ui
服務器端:spa
修改BookServlet_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 "BookServlet.h" #include <thrift/protocol/TBinaryProtocol.h> #include <thrift/server/TSimpleServer.h> #include <thrift/transport/TServerSocket.h> #include <thrift/transport/TBufferTransports.h> 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 ::example; class BookServletHandler : virtual public BookServletIf { public: BookServletHandler() { // Your initialization goes here } bool Sender(const std::vector< ::example::Book_Info> & books) { // Your implementation goes here std::vector< ::example::Book_Info>::const_iterator iter; for(iter=books.begin();iter!=books.end();iter++) { printf("books size: %d\n",books.size()); printf("send 1: %d, %s, %s\n",(*iter).book_id,(*iter).book_name.c_str(),(*iter).book_author.c_str()); } return true; } void Sender2(const std::vector< ::example::Book_Info> & books) { // Your implementation goes here std::vector< ::example::Book_Info>::const_iterator iter; for(iter=books.begin();iter!=books.end();iter++) { printf("books size: %d\n",books.size()); printf("send 2: %d, %s, %s\n",(*iter).book_id,(*iter).book_name.c_str(),(*iter).book_author.c_str()); } } }; int main(int argc, char **argv) { int port = 9090; shared_ptr<BookServletHandler> handler(new BookServletHandler()); shared_ptr<TProcessor> processor(new BookServletProcessor(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; }
監聽端口9090,所作的事情其實就是將client傳遞過來的books參數的內容打印出來,來驗證是否實現了數據傳遞。而後在服務器端用g++編譯,thrift編譯有點兒惡,一方面須要將各類庫(boost,thrift等)加進來,另外一方面有時候還會有一些uint_32沒有定義什麼的錯誤,還要加一些編譯參數,以下給出一個能夠編譯經過的makefile模板,你們能夠根據本身機器的程序路徑和文件名作相應修改。thrift生成代碼編譯的makefile模板:
BOOST_DIR = /usr/include/boost THRIFT_DIR = /usr/local/include/thrift/ LIB_DIR = /usr/local/lib GEN_SRC = ./book_types.cpp ./rpc_types.cpp ./BookServlet.cpp ./book_constants.cpp ./rpc_constants.cpp default: server server: BookServlet_server.skeleton.cpp g++ -DHAVE_NETINET_IN_H -DHAVE_INTTYPES_H -o CppServer -I${THRIFT_DIR} -I${BOOST_DIR} -I../gen-cpp -L${LIB_DIR} -lthrift BookServlet_server.skeleton.cpp ${GEN_SRC} clean: $(RM) -r CppServer
編譯經過後就在服務器端運行響應的程序./CppServer
客戶端
客戶端代碼,thrift_cli.cpp,一樣放在./gen-cpp文件夾下:
#include "BookServlet.h" #include "book_types.h" #include <protocol/TBinaryProtocol.h> #include <transport/TSocket.h> #include <transport/TBufferTransports.h> #include <vector> using namespace std; using namespace apache::thrift; using namespace apache::thrift::protocol; using namespace apache::thrift::transport; using boost::shared_ptr; using namespace example; //other headers, using namespace int main(int argc, char** argv) { shared_ptr<TTransport> socket(new TSocket("localhost", 9090)); shared_ptr<TTransport> transport(new TBufferedTransport(socket)); shared_ptr<TProtocol> protocol(new TBinaryProtocol(transport)); example::BookServletClient client(protocol); try { transport->open(); vector<example::Book_Info> books; Book_Info ibook; ibook.book_id = 1324; ibook.book_name = "thrift"; ibook.book_price = 22; ibook.book_publisher = "letv"; ibook.book_author = "luis"; books.push_back(ibook); ibook.book_id = 1024; books.push_back(ibook); client.Sender(books); client.Sender2(books); transport->close(); } catch (TException &tx) { printf("iERRORs %s\n", tx.what()); } }
因爲個人服務器和客戶端實在同一臺機器上測的,因此訪問localhost,這個客戶端程序其實就是向books里加了兩個數據,而後client.Sender()和client.Sender2()調用服務器端對應的函數。一樣使用上面的makefile文件(修改一下文件名稱)進行編譯,而後運行./thrift_cli。
運行結果
服務器端接到請求,將客戶端傳遞過來的數據對象books打印了出來,證實rpc成功:
本文只是給了一個thrift簡單可以run起來的應用示例,部分參考了[1]和[2],若是要深刻地使用thrift,瞭解thrift的內部架構和實現就是必須的了。