Thrift初用小結

      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的內部架構和實現就是必須的了。

 

 [1] http://dongxicheng.org/search-engine/thrift-rpc/

 [2] http://blog.csdn.net/sraing/article/details/6372684

相關文章
相關標籤/搜索