天天看點

thrift介紹

原文位址:http s://blog.csdn.net/houjixin/article/details/42778335

相關示例代碼見:http://download.csdn.net/detail/hjx_1000/8374829

一、  Thrift簡單介紹

1.1、  Thrift是什麼?能做什麼?

Thrift是Facebook于2007年開發的跨語言的rpc服架構,提供多語言的編譯功能,并提供多種伺服器工作模式;使用者通過Thrift的IDL(接口定義語言)來描述接口函數及資料類型,然後通過Thrift的編譯環境生成各種語言類型的接口檔案,使用者可以根據自己的需要采用不同的語言開發用戶端代碼和伺服器端代碼。

例如,我想開發一個快速計算的RPC服務,它主要通過接口函數getInt對外提供服務,這個RPC服務的getInt函數使用使用者傳入的參數,經過複雜的計算,計算出一個整形值傳回給使用者;伺服器端使用java語言開發,而調用用戶端可以是java、c、python等語言開發的程式,在這種應用場景下,我們隻需要使用Thrift的IDL描述一下getInt函數(以.thrift為字尾的檔案),然後使用Thrift的多語言編譯功能,将這個IDL檔案編譯成C、java、python幾種語言對應的“特定語言接口檔案”(每種語言隻需要一條簡單的指令即可編譯完成),這樣拿到對應語言的“特定語言接口檔案”之後,就可以開發用戶端和伺服器端的代碼了,開發過程中隻要接口不變,用戶端和伺服器端的開發可以獨立的進行。

Thrift為伺服器端程式提供了很多的工作模式,例如:線程池模型、非阻塞模型等等,可以根據自己的實際應用場景選擇一種工作模式高效地對外提供服務;

1.2、  Thrift的相關網址和資料:

(1)  Thrift的官方網站:http://thrift.apache.org/

(2)  Thrift官方下載下傳位址:http://thrift.apache.org/download

(3)  Thrift官方的IDL示例檔案(自己寫IDL檔案時可以此為參考):

https://git-wip-us.apache.org/repos/asf?p=thrift.git;a=blob_plain;f=test/ThriftTest.thrift;hb=HEAD

(4)  各種環境下搭建Thrift的方法:

http://thrift.apache.org/docs/install/

該頁面中共提供了CentOS\Ubuntu\OS X\Windows幾種環境下的搭建Thrift環境的方法。

二、  Thrift的使用

Thrift提供跨語言的服務架構,這種跨語言主要展現在它對多種語言的編譯功能的支援,使用者隻需要使用IDL描述好接口函數,隻需要一條簡單的指令,Thrift就能夠把按照IDL格式描述的接口檔案翻譯成各種語言版本。其實,說搭建Thrift環境的時候,實際上最麻煩的就是搭建Thrift的編譯環境,Thrift的編譯和通常的編譯一樣經過詞法分析、文法分析等等最終生成對應語言的源碼檔案,為了能夠支援對各種語言的編譯,你需要下載下傳各種語言對應的編譯時使用的包;

2.1、  搭建Thrift的編譯環境

本節主要介紹如何搭建Unix編譯環境,搭建時有以下要求:

基本要求:

G++、boost、lex、yacc

源碼安裝要求:

如果使用源碼安裝的方式,則還需要下列工具:

Autoconf、automake、libtool、pkg-config、lex和yacc的開發版、libssl-dev

語言要求:

搭建C++編譯環境:boost、libevent、zlib

搭建java編譯環境:jdk、ApacheAnt

具體搭建環境時可以參考“一”中所列官網的安裝方法。

2.2、  搭建JAVA下Thrift開發環境

在java環境下開發thrift的用戶端或者伺服器程式非常簡單,隻需在工程檔案中加上下面三個jar包(版本可能随時有更新):

  libthrift-0.9.1.jar

  slf4j-api-1.7.5.jar

             slf4j-simple.jar

2.3、  編寫IDL檔案

使用Thrift開發程式,首先要做的事情就是使用IDL對接口進行描述, 然後再使用Thrift的多語言編譯能力将接口的描述檔案編譯成對應語言的版本,本文中将IDL對接口的描述檔案稱為“Thrift檔案”。

(1)  編寫Thrift檔案

使用IDL對接口進行描述的thrift檔案命名一般都是以“.thrift”作為字尾:XXX.thrift,可以在該檔案的開頭為該檔案加上命名空間限制,格式為:namespace語言 命名空間的名字;例如:

namespace javacom.test.service

IDL檔案中對所有接口函數的描述都放在service中,service的名字可以自己指定,該名字也将被用作生成的特定語言接口檔案的名字,接口函數需要對參數使用序号标号,除最後一個接口函數外,要以“,”結束對函數的描述。

例如,下面一個IDL描述的Thrift檔案(該Thrift檔案的檔案名為:test_service.thrift)的全部内容:

namespace java com.test.service

include "thrift_datatype.thrift"

service TestThriftService
{

	/**
	*value 中存放兩個字元串拼接之後的字元串
	*/
	thrift_datatype.ResultStr getStr(:string srcStr1, :string srcStr2),
	
	thrift_datatype.ResultInt getInt(:i32 val)
	
}
           

代碼2.1

這裡的TestThriftService就被用作生成的特定語言的檔案名,例如我想用該Thrift檔案生成一個java版本的接口檔案,那麼生成的java檔案名就是:TestThriftService.java。

(1)  編寫IDL檔案時需要注意的問題

[1]函數的參數要用數字依序标好,序号從1開始,形式為:“序号:參數名”;

[2]每個函數的最後要加上“,”,最後一個函數不加;

[3]在IDL中可以使用添加注釋

(2)  IDL支援的資料類型

IDL大小寫敏感,它共支援以下幾種基本的資料類型:

[1]string, 字元串類型,注意是全部小寫形式;例如:string aString

[2]i16, 16位整形類型,例如:i16 aI16Val;

[3]i32,32位整形類型,對應C/C++/java中的int類型;例如:      I32  aIntVal

[4]i64,64位整形,對應C/C++/java中的long類型;例如:I64 aLongVal

[5]byte,8位的字元類型,對應C/C++中的char,java中的byte類型;例如:byte aByteVal

[6]bool, 布爾類型,對應C/C++中的bool,java中的boolean類型; 例如:bool aBoolVal

[7]double,雙精度浮點類型,對應C/C++/java中的double類型;例如:double aDoubleVal

[8]void,空類型,對應C/C++/java中的void類型;該類型主要用作函數的傳回值,例如:void testVoid(),

除上述基本類型外,ID還支援以下類型:

[1]map,map類型,例如,定義一個map對象:map<i32, i32> newmap;

[2]set,集合類型,例如,定義set<i32>對象:set<i32> aSet;

[3]list,連結清單類型,例如,定義一個list<i32>對象:list<i32> aList;

(3)  在Thrift檔案中自定義資料類型

在IDL中支援兩種自定義類型:枚舉類型和結構體類型,具體如下:

[1]enum, 枚舉類型,例如,定義一個枚舉類型:

enum Numberz
{
  ONE = ,
  TWO,
  THREE,
  FIVE = ,
  SIX,
  EIGHT = 
}
           

注意,枚舉類型裡沒有序号

[2]struct,自定義結構體類型,在IDL中可以自己定義結構體,對應C中的struct,c++中的struct和class,java中的class。例如:

struct TestV1 {
       : i32 begin_in_both,
       : string old_string,
       : i32 end_in_both
}
           

注意,在struct定義結構體時需要對每個結構體成員用序号辨別:“序号: ”。

(4)  定義類型别名

Thrift的IDL支援C/C++中類似typedef的功能,例如:

typedef

i32  Integer 

就可以為i32類型重新起個名字Integer。

2.4、  生成Thrift服務接口檔案

搭建Thrift編譯環境之後,使用下面指令即可将IDL檔案編譯成對應語言的接口檔案:

thrift --gen <language> <Thrift filename>

例如:如果使用上面的thrift檔案(見上面的代碼2.1):test_service.thrift生成一個java語言的接口檔案,則隻需在搭建好thrift編譯環境的機子上,執行如下指令即可:

thrift --gen java test_service.thrift

這裡,我直接在test_service.thrift檔案所在的目錄下執行的指令,是以直接使用檔案名即可(如圖2.1的标号1所示),如果不在test_service.thrift所在的目錄中,則需要具體指明該檔案所在的路徑。

thrift介紹

圖2.1 

如圖2.1 中标号2所示,生成的gen-java的目錄,目錄下面有com、test、service三級目錄,這三級目錄也是根據test_service.thrift檔案中命名空間的名字:com.test.service生成的,進入目錄之後可以看到生成的java語言的接口檔案名為:TestThriftService.java,這個檔案的名字也是根據test_service.thrift檔案的service名字來生成的(見代碼2.1)。

2.5、  編寫伺服器端的java代碼

編寫thrift伺服器程式需要首先完成下面兩步工作:

(1)先将2.2節中的三個jar包添加到工程裡,如圖2.2的标号2所示。

(2)将生成的java接口檔案TestThriftService.java拷貝到自己的工程檔案中,如圖2.2的标号1所示。

thrift介紹

圖2.2

通路器程式需實作TestThriftService.Iface接口,在實作接口中完成自己要提供的服務,伺服器端對服務接口實作的代碼如下所示:

  1. package com.test.service;
  2. import org.apache.thrift.TException;
  3. public class TestThriftServiceImpl implements TestThriftService.Iface
  4. {
  5. @Override
  6. public String getStr(String srcStr1, String srcStr2) throws TException {
  7. long startTime = System.currentTimeMillis();
  8. String res = srcStr1 + srcStr2;
  9. long stopTime = System.currentTimeMillis();
  10. System.out.println( "[getStr]time interval: " + (stopTime-startTime));
  11. return res;
  12. }
  13. @Override
  14. public int getInt(int val) throws TException {
  15. long startTime = System.currentTimeMillis();
  16. int res = val * ;
  17. long stopTime = System.currentTimeMillis();
  18. System.out.println( "[getInt]time interval: " + (stopTime-startTime));
  19. return res;
  20. }
  21. }

代碼2.2

伺服器端啟動thrift服務架構的程式如下所示,在本例中伺服器采用TNonblockingServer工作模式:

  1. package com.test.service;
  2. import org.apache.thrift.TProcessor;
  3. import org.apache.thrift.protocol.TBinaryProtocol;
  4. import org.apache.thrift.server.TNonblockingServer;
  5. import org.apache.thrift.server.TServer;
  6. import org.apache.thrift.transport.TFramedTransport;
  7. import org.apache.thrift.transport.TNonblockingServerSocket;
  8. import org.apache.thrift.transport.TTransportException;
  9. public class testMain {
  10. private static int m_thriftPort = ;
  11. private static TestThriftServiceImpl m_myService = new TestThriftServiceImpl();
  12. private static TServer m_server = null;
  13. private static void createNonblockingServer() throws TTransportException
  14. {
  15. TProcessor tProcessor = new TestThriftService.Processor<TestThriftService.Iface>(m_myService);
  16. TNonblockingServerSocket nioSocket = new TNonblockingServerSocket(m_thriftPort);
  17. TNonblockingServer.Args tnbArgs = new TNonblockingServer.Args(nioSocket);
  18. tnbArgs.processor(tProcessor);
  19. tnbArgs.transportFactory( new TFramedTransport.Factory());
  20. tnbArgs.protocolFactory( new TBinaryProtocol.Factory());
  21. // 使用非阻塞式IO,服務端和用戶端需要指定TFramedTransport資料傳輸的方式
  22. m_server = new TNonblockingServer(tnbArgs);
  23. }
  24. public static boolean start()
  25. {
  26. try {
  27. createNonblockingServer();
  28. } catch (TTransportException e) {
  29. System.out.println( "start server error!" + e);
  30. return false;
  31. }
  32. System.out.println( "service at port: " + m_thriftPort);
  33. m_server.serve();
  34. return true;
  35. }
  36. public static void main(String[] args)
  37. {
  38. if(!start())
  39. {
  40. System.exit( );
  41. }
  42. }
  43. }

代碼2.3

在伺服器端啟動thrift架構的部分代碼比較簡單,不過在寫這些啟動代碼之前需要先确定伺服器采用哪種工作模式對外提供服務,Thrift對外提供幾種工作模式,例如:TSimpleServer、TNonblockingServer、TThreadPoolServer、TThreadedSelectorServer等模式,每種服務模式的通信方式不一樣,是以在服務啟動時使用了那種服務模式,用戶端程式也需要采用對應的通信方式。

另外,Thrift支援多種通信協定格式:TCompactProtocol、TBinaryProtocol、TJSONProtocol等,是以,在使用Thrift架構時,用戶端程式與伺服器端程式所使用的通信協定一定要一緻,否則便無法正常通信。

以上述代碼2.3采用的TNonblockingServer為例,說明伺服器端如何使用Thrift架構,在伺服器端建立并啟動Thrift服務架構的過程為:

[1]為自己的服務實作類定義一個對象,如代碼2.3中的:

TestThriftServiceImplm_myService =newTestThriftServiceImpl();

這裡的TestThriftServiceImpl類就是代碼2.2中我們自己定義的伺服器端對各服務接口的實作類。

[2]定義一個TProcess對象,在根據Thrift檔案生成java源碼接口檔案TestThriftService.java中,Thrift已經自動為我們定義了一個Processor;後續節中将對這個TProcess類的功能進行較長的描述;如代碼2.3中的:

TProcessor tProcessor = NewTestThriftService.Processor<TestThriftService.Iface>(m_myService);

[3]定義一個TNonblockingServerSocket對象,用于tcp的socket通信,如代碼2.3中的:

TNonblockingServerSocketnioSocket = newTNonblockingServerSocket(m_thriftPort);

在建立server端socket時需要指明監聽端口号,即上面的變量:m_thriftPort。

[4]定義TNonblockingServer所需的參數對象TNonblockingServer.Args;并設定所需的參數,如代碼2.3中的:

  1. TNonblockingServer.Args tnbArgs = new TNonblockingServer.Args(nioSocket);
  2. tnbArgs.processor(tProcessor);
  3. tnbArgs.transportFactory( new TFramedTransport.Factory());
  4. tnbArgs.protocolFactory( new TBinaryProtocol.Factory());

在TNonblockingServer模式下我們使用二進制協定:TBinaryProtocol,通信方式采用TFramedTransport,即以幀的方式對資料進行傳輸。

[5]定義TNonblockingServer對象,并啟動該服務,如代碼2.3中的:

m_server = new TNonblockingServer(tnbArgs);

m_server.serve();

2.6、  編寫用戶端代碼

Thrift的用戶端代碼同樣需要伺服器開頭的那兩步:添加三個jar包和生成的java接口檔案TestThriftService.java。

  1. m_transport = new TSocket(THRIFT_HOST, THRIFT_PORT, );
  2. TProtocol protocol = new TBinaryProtocol(m_transport);
  3. TestThriftService.Client testClient = new TestThriftService.Client(protocol);
  4. try {
  5. m_transport.open();
  6. String res = testClient.getStr( "test1", "test2");
  7. System.out.println( "res = " + res);
  8. m_transport.close();
  9. } catch (TException e){
  10. // TODO Auto-generated catch block
  11. e.printStackTrace();
  12. }

代碼2.4

由代碼2.4可以看到編寫用戶端代碼非常簡單,隻需下面幾步即可:

[1]建立一個傳輸層對象(TTransport),具體采用的傳輸方式是TFramedTransport,要與伺服器端保持一緻,即:

m_transport =new TFramedTransport(newTSocket(THRIFT_HOST,THRIFT_PORT, 2000));

這裡的THRIFT_HOST, THRIFT_PORT分别是Thrift伺服器程式的主機位址和監聽端口号,這裡的2000是socket的通信逾時時間;

[2]建立一個通信協定對象(TProtocol),具體采用的通信協定是二進制協定,這裡要與伺服器端保持一緻,即:

TProtocolprotocol =new TBinaryProtocol(m_transport);

[3]建立一個Thrift用戶端對象(TestThriftService.Client),Thrift的用戶端類TestThriftService.Client已經在檔案TestThriftService.java中,由Thrift編譯器自動為我們生成,即:

TestThriftService.ClienttestClient =new TestThriftService.Client(protocol);

[4]打開socket,建立與伺服器直接的socket連接配接,即:

m_transport.open();

[5]通過用戶端對象調用伺服器服務函數getStr,即:

String res = testClient.getStr("test1","test2");

                System.out.println("res = " +res);

[6]使用完成關閉socket,即:

m_transport.close();

        這裡有以下幾點需要說明:

[1]在同步方式使用用戶端和伺服器的時候,socket是被一個函數調用獨占的,不能多個調用同時使用一個socket,例如通過m_transport.open()打開一個socket,此時建立多個線程同時進行函數調用,這時就會報錯,因為socket在被一個調用占着的時候不能再使用;

[2]可以分時多次使用同一個socket進行多次函數調用,即通過m_transport.open()打開一個socket之後,你可以發起一個調用,在這個次調用完成之後,再繼續調用其他函數而不需要再次通過m_transport.open()打開socket;

2.7、  需要注意的問題

(1)Thrift的伺服器端和用戶端使用的通信方式要一樣,否則便無法進行正常通信;

Thrift的伺服器端的種模式所使用的通信方式并不一樣,是以,伺服器端使用哪種通信方式,用戶端程式也要使用這種方式,否則就無法進行正常通信了。例如,上面的代碼2.3中,伺服器端使用的工作模式為TNonblockingServer,在該工作模式下需要采用的傳輸方式為TFramedTransport,也就是在通信過程中會将tcp的位元組流封裝成一個個的幀,此時就需要用戶端程式也這麼做,否則便會通信失敗。出現如下問題:

伺服器端會爆出如下出錯log:

-- :: ERROR [Thread-] [AbstractNonblockingServer.java:] - Read an invalid frame size of -. Are you using TFramedTransport on the client side?           

用戶端則會報出如下出錯log:

org.apache.thrift.transport.TTransportException: java.net.SocketException: Connection reset
	at org.apache.thrift.transport.TIOStreamTransport.read(TIOStreamTransport.java:)
	at org.apache.thrift.transport.TTransport.readAll(TTransport.java:)
	at org.apache.thrift.protocol.TBinaryProtocol.readAll(TBinaryProtocol.java:)
	at org.apache.thrift.protocol.TBinaryProtocol.readI32(TBinaryProtocol.java:)
	at org.apache.thrift.protocol.TBinaryProtocol.readMessageBegin(TBinaryProtocol.java:)
	at org.apache.thrift.TServiceClient.receiveBase(TServiceClient.java:)
	at com.browan.freepp.dataproxy.service.DataProxyService$Client.recv_addLiker(DataProxyService.java:)
	at com.browan.freepp.dataproxy.service.DataProxyService$Client.addLiker(DataProxyService.java:)
	at com.browan.freepp.dataproxy.service.DataProxyServiceTest.test_Likers(DataProxyServiceTest.java:)
	at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
	at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:)
	at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:)
	at java.lang.reflect.Method.invoke(Method.java:)
	at org.junit.runners.model.FrameworkMethod$runReflectiveCall(FrameworkMethod.java:)
	at org.junit.internal.runners.model.ReflectiveCallable.run(ReflectiveCallable.java:)
	at org.junit.runners.model.FrameworkMethod.invokeExplosively(FrameworkMethod.java:)
	at org.junit.internal.runners.statements.InvokeMethod.evaluate(InvokeMethod.java:)
	at org.junit.runners.ParentRunner.runLeaf(ParentRunner.java:)
	at org.junit.runners.BlockJUnit4ClassRunner.runChild(BlockJUnit4ClassRunner.java:)
	at org.junit.runners.BlockJUnit4ClassRunner.runChild(BlockJUnit4ClassRunner.java:)
	at org.junit.runners.ParentRunner$run(ParentRunner.java:)
	at org.junit.runners.ParentRunner$schedule(ParentRunner.java:)
	at org.junit.runners.ParentRunner.runChildren(ParentRunner.java:)
	at org.junit.runners.ParentRunner.access$(ParentRunner.java:)
	at org.junit.runners.ParentRunner$evaluate(ParentRunner.java:)
	at org.junit.runners.ParentRunner.run(ParentRunner.java:)
	at org.eclipse.jdt.internal.junit4.runner.JUnit4TestReference.run(JUnit4TestReference.java:)
	at org.eclipse.jdt.internal.junit.runner.TestExecution.run(TestExecution.java:)
	at org.eclipse.jdt.internal.junit.runner.RemoteTestRunner.runTests(RemoteTestRunner.java:)
	at org.eclipse.jdt.internal.junit.runner.RemoteTestRunner.runTests(RemoteTestRunner.java:)
	at org.eclipse.jdt.internal.junit.runner.RemoteTestRunner.run(RemoteTestRunner.java:)
	at org.eclipse.jdt.internal.junit.runner.RemoteTestRunner.main(RemoteTestRunner.java:)
Caused by: java.net.SocketException: Connection reset
	at java.net.SocketInputStream.read(SocketInputStream.java:)
	at java.net.SocketInputStream.read(SocketInputStream.java:)
	at java.io.BufferedInputStream.fill(BufferedInputStream.java:)
	at java.io.BufferedInputStream.read1(BufferedInputStream.java:)
	at java.io.BufferedInputStream.read(BufferedInputStream.java:)
	at org.apache.thrift.transport.TIOStreamTransport.read(TIOStreamTransport.java:)
	...  more
           

(2)在伺服器端或者用戶端直接使用IDL生成的接口檔案時,可能會遇到下面兩個問題:

[1]、Cannotreduce the visibility of the inherited method fromProcessFunction<I,TestThriftService.getStr_args>

[2]、The typeTestThriftService.Processor<I>.getStr<I> must implement theinherited abstract methodProcessFunction<I,TestThriftService.getStr_args>.isOneway()

問題産生的原因:

問題[1] 是繼承類的通路權限縮小所造成的;

問題[2] 是因為存在抽象函數isOneWay所緻;

解決辦法:

問題[1]的通路權限由protected修改為public;

問題[2]的解決辦法是為抽象函數添加一個空的函數體即可。

2.8、  一些的應用技巧

(1)  為調用加上一個事務ID

在分布式服務開發過程中,一次事件(事務)的執行可能跨越位于不同機子上多個服務程式,在後續維護過程中跟蹤log将變得非常麻煩,是以在系統設計的時候,系統的一個事務産生之處應該産生一個系統唯一的事務ID,該ID在各服務程式之間進行傳遞,讓一次事務在所有服務程式輸出的log都以此ID作為辨別。

在使用Thrift開發伺服器程式的時候,也應該為每個接口函數提供一個事務ID的參數,并且在伺服器程式開發過程中,該ID應該在内部函數調用過程中也進行傳遞,并且在日志輸出的時候都加上它,以便問題跟蹤。

(2)  封裝傳回結果

Thrift提供的RPC方式的服務,使得調用方可以像調用自己的函數一樣調用Thrift服務提供的函數;在使用Thrift開發過程中,盡量不要直接傳回需要的資料,而是将傳回結果進行封裝,例如上面的例子中的getStr函數就是直接傳回了結果string,見Thrift檔案test_service.thrift中對該函數的描述:

stringgetStr(1:string srcStr1, 2:string srcStr2)

在實際開發過程中,這是一種很不好的行為,在傳回結果為null的時候還可能造成調用方産生異常,需要對傳回結果進行封裝,例如:

/*String類型傳回結果*/
struct ResultStr
{
  : ThriftResult result,
  : string value
}
           

其中ThriftResult是自己定義的枚舉類型的傳回結果,在這裡可以根據自己的需要添加任何自己需要的傳回結果類型:

enum ThriftResult
{
  SUCCESS,           /*成功*/
  SERVER_UNWORKING,  /*伺服器處于非Working狀态*/
  NO_CONTENT,  		 /*請求結果不存在*/
  PARAMETER_ERROR,	 /*參數錯誤*/
  EXCEPTION,	 	 /*内部出現異常*/
  INDEX_ERROR,		 /*錯誤的索引或者下标值*/
  UNKNOWN_ERROR, 	 /*未知錯誤*/
  DATA_NOT_COMPLETE, 	 /*資料不完全*/
  INNER_ERROR, 	 /*内部錯誤*/
}
           

此時可以将上述定義的getStr函數修改為:

ResultStr  getStr(1:string srcStr1, 2:string srcStr2)

在此函數中,任何時候都會傳回一個ResultStr對象,無論異常還是正常情況,在出錯時還可以通過ThriftResult傳回出錯的類型。

(3)  将服務與資料類型分開定義

在使用Thrift開發一些中大型項目的時候,很多情況下都需要自己封裝資料結構,例如前面将傳回結果進行封裝的時候就定義了自己的資料類型ResultStr,此時,将資料結構和服務分開定義到不通的檔案中,可以增加thrift檔案的易讀性。例如:

在thrift檔案:thrift_datatype.thrift中定義資料類型,如:

namespace java com.browan.freepp.thriftdatatype
const string VERSION = "1.0.1"
/**為ThriftResult添加資料不完全和内部錯誤兩種類型
*/

/****************************************************************************************************
* 定義傳回值,
* 枚舉類型ThriftResult,表示傳回結果,成功或失敗,如果失敗,還可以表示失敗原因
* 每種傳回類型都對應一個封裝的結構體,該結構體其命名遵循規則:"Result" + "具體操作結果類型",結構體都包含兩部分内容:
* 第一部分為枚舉類型ThriftResult變量result,表示操作結果,可以 表示成功,或失敗,失敗時可以給出失敗原因
* 第二部分的變量名為value,表示傳回結果的内容;
*****************************************************************************************************/
enum ThriftResult
{
  SUCCESS,           /*成功*/
  SERVER_UNWORKING,  /*伺服器處于非Working狀态*/
  NO_CONTENT,  		 /*請求結果不存在*/
  PARAMETER_ERROR,	 /*參數錯誤*/
  EXCEPTION,	 	 /*内部出現異常*/
  INDEX_ERROR,		 /*錯誤的索引或者下标值*/
  UNKNOWN_ERROR 	 /*未知錯誤*/
  DATA_NOT_COMPLETE 	 /*資料不完全*/
  INNER_ERROR 	 /*内部錯誤*/
}

/*bool類型傳回結果*/
struct ResultBool 
{
  : ThriftResult result,
  : bool value
}

/*int類型傳回結果*/
struct ResultInt
{
  : ThriftResult result,
  : i32 value
}

/*String類型傳回結果*/
struct ResultStr
{
  : ThriftResult result,
  : string value
}

/*long類型傳回結果*/
struct ResultLong
{
  : ThriftResult result,
  : i64 value
}



/*double類型傳回結果*/
struct ResultDouble
{
  : ThriftResult result,
  : double value
}

/*list<string>類型傳回結果*/
struct ResultListStr 
{
  : ThriftResult result,
  : list<string> value
}

/*Set<string>類型傳回結果*/
struct ResultSetStr 
{
  : ThriftResult result,
  : set<string> value
}

/*map<string,string>類型傳回結果*/
struct ResultMapStrStr 
{
  : ThriftResult result,
  : map<string,string> value
}
           

代碼2.5

在另外一個檔案test_service.thrift中定義服務接口函數,如下所示:

namespace java com.test.service

include "thrift_datatype.thrift"
service TestThriftService
{

	/**
	*value 中存放兩個字元串拼接之後的字元串
	*/
	thrift_datatype.ResultStr getStr(<1:string srcStr1, 2:string srcStr2),
	
	thrift_datatype.ResultInt getInt(1:i32 val)
	
}
</code></pre>
           

代碼 2.6

由于在接口服務定義的thrift檔案test_service.thrift中要用到對資料類型定義的thrift檔案:thrift_datatype.thrift,是以需要在其檔案前通過include把自己所使用的thrift檔案包含進來,另外在使用其他thrift檔案中定義的資料類型時要加上它的檔案名,如:thrift_datatype.ResultStr

(4)  為Thrift檔案添加版本号

在實際開發過程中,還可以為Thrift檔案加上版本号,以友善對thrift的版本進行控制,如代碼2.5所示。

相關示例代碼見:http://download.csdn.net/detail/hjx_1000/8374829