Thrift框架調研
u 簡介
Thrift是一種開源的跨語言的RPC服務框架。Thrift最初由facebook公司開發的,在2007年facebook將其提交apache基金會開源了。對于當時的facebook來說創造thrift是為了解決facebook系統中各系統間大數據量的傳輸通信以及系統之間語言環境不同需要跨平臺的特性。所以thrift可以支持多種程序語言,支持的語言如下:

在多種不同的語言之間通信thrift可以作為二進制的高性能的通訊中間件,支持數據(對象)序列化和多種類型的RPC服務。Thrift是IDL(interface definition language)描述性語言的一個具體實現,Thrift適用于程序對程序靜態的數據交換,需要先確定好他的數據結構,他是完全靜態化的,當數據結構發生變化時,必須重新編輯IDL文件,代碼生成,再編譯載入的流程,跟其他IDL工具相比較可以視為是Thrift的弱項,Thrift適用于搭建大型數據交換及存儲的通用工具,對于大型系統中的子系統間數據傳輸相對于JSON和xml無論在性能、傳輸大小上有明顯的優勢。

u 基礎架構
如下圖所示是thrift的協議棧整體的架構,thrift是一個客戶端和服務器端的架構體系(c/s),在最上層是用戶自行實現的業務邏輯代碼。 第二層是由thrift編譯器自動生成的代碼,主要用于結構化數據的解析,發送和接收。TServer主要任務是高效的接受客戶端請求,并將請求轉發給Processor處理。Processor負責對客戶端的請求做出響應,包括RPC請求轉發,調用參數解析和用戶邏輯調用,返回值寫回等處理。從TProtocol以下部分是thirft的傳輸協議和底層I/O通信。TProtocol是用于數據類型解析的,將結構化數據轉化為字節流給TTransport進行傳輸。TTransport是與底層數據傳輸密切相關的傳輸層,負責以字節流方式接收和發送消息體,不關注是什么數據類型。底層IO負責實際的數據傳輸,包括socket、文件和壓縮數據流等。
協議層TProtocol:
在傳輸協議上總體上劃分為文本(text)和二進制(binary)傳輸協議, 為節約帶寬,提供傳輸效率,一般情況下使用二進制類型的傳輸協議為多數。
1>TBinaryProtocol – 二進制編碼格式進行數據傳輸。
2>TCompactProtocol – 高效的編碼方式,使用類似于protobuffer的Variable-Length Quantity (VLQ) 編碼(可以節省傳輸空間,使數據的傳輸效率更高)對數據進行壓縮。 關于VLQ了解更多(http://en.wikipedia.org/wiki/Variable-length_quantity)
3>TJSONProtocol – 使用JSON的數據編碼協議進行數據傳輸。
4>TSimpleJSONProtocol – 這種節約只提供JSON只寫的協議,適用于通過腳本語言解析
5>TDebugProtocol – 在開發的過程中幫助開發人員調試用的,以文本的形式展現方便閱讀。
傳輸層TTransport
1>TSocket- 使用阻塞式I/O進行傳輸,也是最常見的模式。
2>TFramedTransport- 使用非阻塞方式,按塊的大小,進行傳輸,類似于Java中的NIO。
3>TFileTransport- 顧名思義按照文件的方式進程傳輸,雖然這種方式不提供Java的實現,但是實現起來非常簡單。
4>TMemoryTransport- 使用內存I/O,就好比Java中的ByteArrayOutputStream實現。
5>TZlibTransport- 使用執行zlib壓縮,不提供Java的實現。
6>TNonblockingTransport-使用非阻塞方式,用于構建異步客戶端。
服務端類型 :
1>TSimpleServer - 單線程服務器端使用標準的阻塞式I/O。
2>TThreadPoolServer - 多線程服務器端使用標準的阻塞式I/O。
3>TNonblockingServer – 多線程服務器端使用非阻塞式I/O,并且實現了Java中的NIO通道。
u 數據類型
Thrift 腳本可定義的數據類型包括以下幾種類型:
- 基本類型:
- bool:布爾值,true 或 false,對應 Java 的 boolean
- byte:8 位有符號整數,對應 Java 的 byte
- i16:16 位有符號整數,對應 Java 的 short
- i32:32 位有符號整數,對應 Java 的 int
- i64:64 位有符號整數,對應 Java 的 long
- double:64 位浮點數,對應 Java 的 double
- string:未知編碼文本或二進制字符串,對應 Java 的 String
- 結構體類型:
- struct:定義公共的對象,類似于 C 語言中的結構體定義,在 Java 中是一個 JavaBean
- 容器類型:
- list:對應 Java 的 ArrayList
- set:對應 Java 的 HashSet
- map:對應 Java 的 HashMap
- 異常類型:
- exception:對應 Java 的 Exception
- 服務類型:service:對應服務的類
u 安裝使用
在Windows下安裝前需要很多依賴包的安裝,比較麻煩。下面介紹了在Linux下的源碼安裝,直接wget或手動下載最tar.gz安裝包編譯安裝:
tar -xvf thrift-0.9.1.tar.gz cd thrift-0.9.1 ./configure make make install |
使用命令thrift -version,顯示Thrift version 0.9.1 則表示安裝成功。
我們使用到了java,所以通過ant方式構建thrift相關lib包,進入$THRIFT_HOME/lib/java目錄下,在ant編譯libthrift出現了如下問題:
Buildfile: /letv/apps_install/thrift-0.9.1/lib/java/build.xml setup.init: mvn.ant.tasks.check: proxy: mvn.ant.tasks.download: [get] Getting: http://repo1.maven.org/maven2/org/apache/maven/maven-ant-tasks/2.1.3/maven-ant-tasks-2.1.3.jar [get] To: /letv/apps_install/thrift-0.9.1/lib/java/build/tools/maven-ant-tasks-2.1.3.jar [get] Not modified - so not downloaded mvn.init: Unable to obtain resource from /letv/apps_install/thrift-0.9.1/lib/java/build/tools/maven-ant-tasks-2.1.3.jar: java.util.zip.ZipException: error in opening zip file [typedef] Unable to obtain resource from /letv/apps_install/thrift-0.9.1/lib/java/build/tools/maven-ant-tasks-2.1.3.jar: [typedef] java.util.zip.ZipException: error in opening zip file [typedef] at java.util.zip.ZipFile.open(Native Method) [typedef] at java.util.zip.ZipFile.<init>(ZipFile.java:214) [typedef] at java.util.zip.ZipFile.<init>(ZipFile.java:144) [typedef] at java.util.jar.JarFile.<init>(JarFile.java:153) [typedef] at java.util.jar.JarFile.<init>(JarFile.java:117) [typedef] at org.apache.tools.ant.AntClassLoader.getResourceURL(AntClassLoader.java:1014) [typedef] at org.apache.tools.ant.AntClassLoader$ResourceEnumeration.findNextResource(AntClassLoader.java:150) [typedef] at org.apache.tools.ant.AntClassLoader$ResourceEnumeration.<init>(AntClassLoader.java:111) [typedef] at org.apache.tools.ant.AntClassLoader.findResources(AntClassLoader.java:954) [typedef] at org.apache.tools.ant.AntClassLoader.getNamedResources(AntClassLoader.java:923) [typedef] at org.apache.tools.ant.loader.AntClassLoader5.getResources(AntClassLoader5.java:58) [typedef] at org.apache.tools.ant.taskdefs.Definer.resourceToURLs(Definer.java:360) [typedef] at org.apache.tools.ant.taskdefs.Definer.execute(Definer.java:246) [typedef] at org.apache.tools.ant.UnknownElement.execute(UnknownElement.java:292) [typedef] at sun.reflect.GeneratedMethodAccessor4.invoke(Unknown Source) [typedef] at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43) [typedef] at java.lang.reflect.Method.invoke(Method.java:601) [typedef] at org.apache.tools.ant.dispatch.DispatchUtils.execute(DispatchUtils.java:106) [typedef] at org.apache.tools.ant.Task.perform(Task.java:348) [typedef] at org.apache.tools.ant.Target.execute(Target.java:435) [typedef] at org.apache.tools.ant.Target.performTasks(Target.java:456) [typedef] at org.apache.tools.ant.Project.executeSortedTargets(Project.java:1393) [typedef] at org.apache.tools.ant.Project.executeTarget(Project.java:1364) [typedef] at org.apache.tools.ant.helper.DefaultExecutor.executeTargets(DefaultExecutor.java:41) [typedef] at org.apache.tools.ant.Project.executeTargets(Project.java:1248) [typedef] at org.apache.tools.ant.Main.runBuild(Main.java:851) [typedef] at org.apache.tools.ant.Main.startAnt(Main.java:235) [typedef] at org.apache.tools.ant.launch.Launcher.run(Launcher.java:280) [typedef] at org.apache.tools.ant.launch.Launcher.main(Launcher.java:109) [typedef] Could not load definitions from resource org/apache/maven/artifact/ant/antlib.xml. It could not be found. BUILD FAILED /letv/apps_install/thrift-0.9.1/lib/java/build.xml:279: Problem: failed to create task or type antlib:org.apache.maven.artifact.ant:remoteRepository Cause: The name is undefined. Action: Check the spelling. Action: Check that any custom tasks/types have been declared. Action: Check that any <presetdef>/<macrodef> declarations have taken place. No types or tasks have been defined in this namespace yet This appears to be an antlib declaration. Action: Check that the implementing library exists in one of: -/letv/apps_install/apache-ant-1.9.4/lib -/root/.ant/lib -a directory added on the command line with the -lib argument Total time: 2 seconds |
解決方法:ant通過yum方式安裝,默認版本1.7.1,自行安裝最新版本1.9.4,成功編譯后在當前目錄生成了build文件夾,包括libthrift-0.9.1.jar以及依賴的lib:
commons-codec-1.6.jar
commons-lang3-3.1.jar
commons-logging-1.1.1.jar
httpclient-4.2.5.jar
httpcore-4.2.4.jar
junit-4.4.jar
log4j-1.2.14.jar
servlet-api-2.5.jar
slf4j-api-1.5.8.jar
slf4j-log4j12-1.5.8.jar
u 開發流程及代碼示例

1,編寫IDL文件helloService.thrift,如下:
/**
* Hello world Testing
*
*/
namespace java com.le.mms
service HelloService {
i32 sayInt(1:i32 param)
string sayString(1:string param)
bool sayBoolean(1:bool param)
void sayVoid()
}
2. 使用thrift編譯器生成所需語言的代碼
thrift --gen java helloService.thrift
在當前目錄下生成gen-java目錄,里面生成了HelloService.java文件。
Thrift --gen py helloService.thrift 可以生成python相關代碼。
3. 新建Java工程或maven工程,java工程引入ant生成的lib,maven工程pom.xml中引入依賴。
<dependency>
<groupId>org.apache.thrift</groupId>
<artifactId>libthrift</artifactId>
<version>0.9.1</version>
</dependency>
自動會將依賴的lib下載下來。
4. 編寫HelloServiceImpl.java業務實現類. 需要實現HelloService.Iface接口,由thrift自動生成的代碼。
package com.le.mms.thrift; import org.apache.thrift.TException; public class HelloServiceImpl implements HelloService.Iface{ @Override public int sayInt(int param) throws TException { System.out.println("say int :" + param); return param; } @Override public String sayString(String param) throws TException { System.out.println("say string :" + param); return param; } @Override public boolean sayBoolean(boolean param) throws TException { System.out.println("say boolean :" + param); return param; } @Override public void sayVoid() throws TException { System.out.println("say void ..."); } } |
5. 編寫thrift-java服務器端,監聽端口9090
package com.le.mms.thrift.server; import org.apache.thrift.protocol.TBinaryProtocol; import org.apache.thrift.protocol.TBinaryProtocol.Factory; import org.apache.thrift.server.TServer; import org.apache.thrift.server.TSimpleServer; import org.apache.thrift.transport.TServerSocket; import org.apache.thrift.transport.TTransportException; import com.le.mms.thrift.HelloService; import com.le.mms.thrift.HelloServiceImpl; import com.le.mms.thrift.HelloService.Processor; /** * 啟動thrift的java服務器端 * * @author david * */ public class HelloServiceServer { public static void main(String[] args) { try { // 設置服務器端口 TServerSocket serverTransport = new TServerSocket(9090); // 設置二進制協議工廠 Factory protocolFactory = new TBinaryProtocol.Factory(); //處理器關聯業務實現 Processor<HelloService.Iface> processor = new HelloService.Processor<HelloService.Iface>(new HelloServiceImpl()); // 1. 使用單線程標準阻塞I/O模型 TServer.Args simpleArgs = new TServer.Args(serverTransport); simpleArgs.processor(processor); simpleArgs.protocolFactory(protocolFactory); TServer server = new TSimpleServer(simpleArgs); // 2. 使用線程池服務模型 // TThreadPoolServer.Args poolArgs = new TThreadPoolServer.Args(serverTransport); // poolArgs.processor(processor); // poolArgs.protocolFactory(protocolFactory); // TServer poolServer = new TThreadPoolServer(poolArgs); // poolServer.serve(); System.out.println("開啟thrift服務器,監聽端口:9090"); server.serve(); } catch (TTransportException e) { e.printStackTrace(); } } } |
6.編寫thrift-java客戶端,發出請求。
package com.le.mms.thrift.client; import org.apache.thrift.TException; import org.apache.thrift.protocol.TBinaryProtocol; import org.apache.thrift.protocol.TProtocol; import org.apache.thrift.transport.TSocket; import org.apache.thrift.transport.TTransport; import org.apache.thrift.transport.TTransportException; import com.le.mms.thrift.HelloService; /** * 調用thrift的java客戶端 * * @author david * */ public class HelloServiceClient { public static void main(String[] args) { try { // 設置調用的服務地址-端口 TTransport transport = new TSocket("localhost", 9090); // 使用二進制協議 TProtocol protocol = new TBinaryProtocol(transport); // 使用的接口 HelloService.Client client = new HelloService.Client(protocol); //打開socket transport.open(); client.sayBoolean(true); client.sayString("Hello world"); client.sayInt(20141111); client.sayVoid(); transport.close(); } catch (TTransportException e) { e.printStackTrace(); } catch (TException te) { te.printStackTrace(); } } } |
以上編寫完了server和client端代碼,運行server開啟監聽。然后運行client就能夠與server端進行rpc調用方式的通訊了。
posted on 2014-12-03 23:56
David1228 閱讀(28338)
評論(0) 編輯 收藏 所屬分類:
JAVA 、
J2EE