Apache Thrift的簡單使用

Apache Thrift的簡單使用

----------------------

 

1. 簡單介紹

Thrift是Facebook的一個開源項目,主要是一個跨語言的服務開發框架。它有一個代碼生成器來對它所定義的IDL定義文件自動生成服務代碼框架。用戶只要在其之前進行二次開發就行,對於底層的RPC通訊等都是透明的。目前它支持的語言有C++, Java, Python, PHP, Ruby, Erlang, Perl, Haskell, C#, Cocoa, Smalltalk, and OCaml.

 

2. 下載與安裝

可以在http://incubator.apache.org/thrift/download/去下載它的最新版本,目前最新版本是0.5.0。另外你也可以check出它的svn,方法如下:

svn co http://svn.apache.org/repos/asf/thrift/trunk thrift

cd thrift

 

在它的jira中看到,它的0.6版本也很快就會出來了。

 

我的本本是debian 6.0,如果用ubuntu的兄弟安裝方法也是一樣的

[c-sharp] view plaincopy
  1. tar -zxvf thrift-0.5.0.tar.gz  
  2. cd thrift-0.5.0  
  3. ./configure  
  4. make   
  5. sudo make install  
 

 

這時thrift的代碼生成器和一些庫文件就生成好了。

 

你可以用如下命令看一下thrift的版本信息

 

[c-sharp] view plaincopy
  1. thrift -version  
 

 

3. 一個簡單的例子

在thrift源代碼目錄有一個叫tutorial的目錄,進行其中後運行thrift命令生成相應的服務代碼:

[c-sharp] view plaincopy
  1. $ thrift -r --gen cpp tutorial.thrift // -r對其中include的文件也生成服務代碼 -gen是生成服務代碼的語言  
 

 

運行完之後會在當前目錄看到一個gen-cpp目錄,其中就是thrfit命令生成的代碼

 

這時你cd到tutorial/cpp目錄,運行make,生成相應的CppServer與CppClient程式。

 

這時你可以用./CppServer運行服務端,讓其監聽一個特定的端口

 

這時你可以用./CppClient運行客戶端程式,讓其去連接服務端,調用其所對應的服務。默認調用後會輸出如下信息:

 

[c-sharp] view plaincopy
  1. lemo@debian:~/Workspace/Facebook/Thrift/thrift-0.5.0/tutorial/cpp$ ./CppServer  
  2. Starting the server...  
  3. ping()  
  4. add(1,1)  
  5. calculate(1,{4,1,0})  
  6. calculate(1,{2,15,10})  
  7. getStruct(1)  
 

 

如果你的終端中也出現瞭如上的信息,恭喜你,運行成功了。如果在運行CppServer的時候找不到動態庫,看看你是不是運行了make install,如果運行了,再運行一下sudo ldconfig試試。再用ldd CppServer看一下它有沒有找到相應的動態庫了。

 

4. 例子分析

 

4.1 Thrift IDL的分析

 

這邊有兩個IDL文件,內容如下:

 

[c-sharp] view plaincopy
  1. shared.thrift  
  2. ---------------  
  3. **  
  4.  * This Thrift file can be included by other Thrift files that want to share  
  5.  * these definitions.  
  6.  */  
  7. namespace cpp shared  
  8. namespace java shared  
  9. namespace perl shared  
  10. // 這裏定義了一個結構體,沒有定義方法,對應於生成的代碼在gen-cpp中的shared_types.h中,其中有一個class叫SharedStruct,  
  11. // 有沒有看到其中有兩個方法叫read和write,這就是用來對其進行序列化與把序列化的方法.  
  12. // 對了,其中的i32是Thrift IDL中定義的變量類型,對應於c++語言中的int32_t  
  13. struct SharedStruct {      
  14.   1: i32 key  
  15.   2: string value  
  16. }  
  17. // 這裏定義的一個服務,它語義上類似於面向對象中的定義一個接口,thrift的編譯器會對其產生一套實現其接口的客戶端與服務端方法  
  18. // 服務的一般定義格式如下  
  19. // service <name>  
  20. // <returntype> <name>(<arguments>)  
  21. // [ throws (<exceptions>)]  
  22. //   ...  
  23. // }  
  24. service SharedService {  
  25.   SharedStruct getStruct(1: i32 key)  
  26. }  
  27. tutorial.thrift  
  28. ----------------  
  29. /** 
  30.  * Thrift files can reference other Thrift files to include common struct 
  31.  * and service definitions. These are found using the current path, or by 
  32.  * searching relative to any paths specified with the -I compiler flag. 
  33.  * 
  34.  * Included objects are accessed using the name of the .thrift file as a 
  35.  * prefix. i.e. shared.SharedObject 
  36.  */  
  37.  // 這個IDL包含了另一個IDL,也就是說另一個IDL中的對象與服務對其時可見的  
  38. include "shared.thrift"  
  39. /** 
  40.  * Thrift files can namespace, package, or prefix their output in various 
  41.  * target languages. 
  42.  */  
  43.  // 這裏定義了一些語言的namespace空間  
  44. namespace cpp tutorial  
  45. namespace java tutorial  
  46. namespace php tutorial  
  47. namespace perl tutorial  
  48. /** 
  49.  * Thrift lets you do typedefs to get pretty names for your types. Standard 
  50.  * C style here. 
  51.  */  
  52.  // 自定義類型  
  53. typedef i32 MyInteger  
  54. /** 
  55.  * Thrift also lets you define constants for use across languages. Complex 
  56.  * types and structs are specified using JSON notation. 
  57.  */  
  58.  // 定義一些變量  
  59. const i32 INT32CONSTANT = 9853  
  60. const map<string,string> MAPCONSTANT = {'hello':'world''goodnight':'moon'}  
  61. /** 
  62.  * You can define enums, which are just 32 bit integers. Values are optional 
  63.  * and start at 1 if not supplied, C style again. 
  64.  */  
  65.  // 定義枚舉類型  
  66. enum Operation {  
  67.   ADD = 1,  
  68.   SUBTRACT = 2,  
  69.   MULTIPLY = 3,  
  70.   DIVIDE = 4  
  71. }  
  72. /** 
  73.  * Structs are the basic complex data structures. They are comprised of fields 
  74.  * which each have an integer identifier, a type, a symbolic name, and an 
  75.  * optional default value. 
  76.  * 
  77.  * Fields can be declared "optional", which ensures they will not be included 
  78.  * in the serialized output if they aren't set.  Note that this requires some 
  79.  * manual management in some languages. 
  80.  */  
  81. struct Work {  
  82.   1: i32 num1 = 0,  
  83.   2: i32 num2,  
  84.   3: Operation op,          
  85.   4: optional string comment, //這裏的optional字段類型表示如果這個字段的值沒有被賦值,它就不會被序列化輸出  
  86. }  
  87. /** 
  88.  * Structs can also be exceptions, if they are nasty. 
  89.  */  
  90.  // 這裏定義了一些異常  
  91. exception InvalidOperation {  
  92.   1: i32 what,  
  93.   2: string why  
  94. }  
  95. /** 
  96.  * Ahh, now onto the cool part, defining a service. Services just need a name 
  97.  * and can optionally inherit from another service using the extends keyword. 
  98.  */  
  99.  // 這裏是定義服務,它繼承了shared的服務  
  100. service Calculator extends shared.SharedService {  
  101.   /** 
  102.    * A method definition looks like C code. It has a return type, arguments, 
  103.    * and optionally a list of exceptions that it may throw. Note that argument 
  104.    * lists and exception lists are specified using the exact same syntax as 
  105.    * field lists in struct or exception definitions. 
  106.    */  
  107.    void ping(),  
  108.    i32 add(1:i32 num1, 2:i32 num2),  
  109.    i32 calculate(1:i32 logid, 2:Work w) throws (1:InvalidOperation ouch),  
  110.    /** 
  111.     * This method has a oneway modifier. That means the client only makes 
  112.     * a request and does not listen for any response at all. Oneway methods 
  113.     * must be void. 
  114.     */  
  115.    oneway void zip()  
  116. }  
 

 

4.2 服務端與客戶端代碼的分析

   4.2.1 c++服務端

   在tutorial/cpp目錄中的CppServer.cpp是它的服務代碼,主要分成兩部分,

   一部分是main方法用於做一些初始化與服務的啓動,第二部分對於IDL中定義的接口的實現

 

[c-sharp] view plaincopy
  1.   int main(int argc, char **argv) {  
  2. // 定義了RPC的協議工廠,這裏使用了二進制協議,你不可以使用別的協議,如JSON,Compact等  
  3. shared_ptr<TProtocolFactory> protocolFactory(new TBinaryProtocolFactory());  
  4. // 這裏生成用戶實現的CalculatorHandler服務,再把其幫定到一個Processor上去,它主要用於處理協議的輸入與輸出流  
  5. shared_ptr<CalculatorHandler> handler(new CalculatorHandler());  
  6. shared_ptr<TProcessor> processor(new CalculatorProcessor(handler));  
  7. // 生成一個傳輸通道,這裏使用了Socket方式  
  8. shared_ptr<TServerTransport> serverTransport(new TServerSocket(9090));  
  9. // 生成一個傳輸工廠,主要用於把上面的transport轉換成一個新的應用層傳輸通道  
  10. shared_ptr<TTransportFactory> transportFactory(new TBufferedTransportFactory());  
  11. // 生成一個簡單的服務端,這是一個單線程的服務端   
  12. TSimpleServer server(processor,  
  13.                       serverTransport,  
  14.                       transportFactory,  
  15.                       protocolFactory);  
  16. // 你也可以生成一個多線程的服務端,就是對其加入線程池。但它現在還不支持進程池,但可能會在0.7版本中進行支持。  
  17.  /** 
  18.   * Or you could do one of these 
  19.  shared_ptr<ThreadManager> threadManager = 
  20.    ThreadManager::newSimpleThreadManager(workerCount); 
  21.  shared_ptr<PosixThreadFactory> threadFactory = 
  22.    shared_ptr<PosixThreadFactory>(new PosixThreadFactory()); 
  23.  threadManager->threadFactory(threadFactory); 
  24.  threadManager->start(); 
  25.  TThreadPoolServer server(processor, 
  26.                           serverTransport, 
  27.                           transportFactory, 
  28.                           protocolFactory, 
  29.                           threadManager); 
  30.  TThreadedServer server(processor, 
  31.                         serverTransport, 
  32.                         transportFactory, 
  33.                         protocolFactory); 
  34.  */  
  35.  printf("Starting the server.../n");  
  36.  server.serve();   // 啓動服務  
  37.  printf("done./n");  
  38.  return 0;  
  39. }  
 

 

 

另一部分如下:

[c-sharp] view plaincopy
  1. // 這一部分主要是實現接口類,用於提供給相應的服務使用。  
  2. class CalculatorHandler : public CalculatorIf {  
  3. public:  
  4.  CalculatorHandler() {}  
  5.  void ping() {  
  6.    printf("ping()/n");  
  7.  }  
  8.  int32_t add(const int32_t n1, const int32_t n2) {  
  9.    ...  
  10.  }  
  11.  int32_t calculate(const int32_t logid, const Work &work) {  
  12.  ...  
  13.  }  
  14.  void getStruct(SharedStruct &ret, const int32_t logid) {  
  15.  ...  
  16.  }  
  17.  void zip() {  
  18.    printf("zip()/n");  
  19.  }  
  20. protected:  
  21.  map<int32_t, SharedStruct> log;  
  22. };  
 

4.2.2 c++客戶端

[c-sharp] view plaincopy
  1. int main(int argc, char** argv) {  
  2.  // 生成一個Socket連接到服務端  
  3.  shared_ptr<TTransport> socket(new TSocket("localhost", 9090));  
  4.  // 對Socket通道加入緩衝功能  
  5.  shared_ptr<TTransport> transport(new TBufferedTransport(socket));  
  6.  // 生成相應的二進制協議,這個要和服務端一致,不然會出現協議版本不對的錯誤  
  7.  shared_ptr<TProtocol> protocol(new TBinaryProtocol(transport));  
  8.  // 生成客戶端的服務對象  
  9.  CalculatorClient client(protocol);  
  10.  try {  
  11.    transport->open(); // 加開服務  
  12.    // 調用事先定義好的服務接口  
  13.    client.ping();  
  14.    printf("ping()/n");  
  15.    int32_t sum = client.add(1,1);  
  16.    printf("1+1=%d/n", sum);  
  17.    Work work;  
  18.    work.op = Operation::DIVIDE;  
  19.    work.num1 = 1;  
  20.    work.num2 = 0;  
  21.    try {  
  22.      int32_t quotient = client.calculate(1, work);  
  23.      printf("Whoa? We can divide by zero!/n");  
  24.    } catch (InvalidOperation &io) {  
  25.      printf("InvalidOperation: %s/n", io.why.c_str());  
  26.    }  
  27.    work.op = Operation::SUBTRACT;  
  28.    work.num1 = 15;  
  29.    work.num2 = 10;  
  30.    int32_t diff = client.calculate(1, work);  
  31.    printf("15-10=%d/n", diff);  
  32.    // Note that C++ uses return by reference for complex types to avoid  
  33.    // costly copy construction  
  34.    SharedStruct ss;  
  35.    client.getStruct(ss, 1);  
  36.    printf("Check log: %s/n", ss.value.c_str());  
  37.    // 關閉服務  
  38.    transport->close();  
  39.  } catch (TException &tx) {  
  40.    printf("ERROR: %s/n", tx.what());  
  41.  }  
  42. }  
 

4.2.3 其它代碼的實現

在tutorial目錄中有其它代碼的例子,如erl,java,python,perl,ruby等。

 

5 參考

1. http://incubator.apache.org/thrift/

2. http://incubator.apache.org/thrift/static/thrift-20070401.pdf

發佈了44 篇原創文章 · 獲贊 9 · 訪問量 25萬+
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章