什么是Thrift
Protobuf是一個語言中立、平臺中立,對結(jié)構(gòu)化數(shù)據(jù)進行序列化的可擴展機制。
我們在開發(fā)的時候開發(fā)了一個restful web service,就是基于rest的http調(diào)用,A系統(tǒng)作為客戶端,B系統(tǒng)作為服務(wù)器端。A系統(tǒng)可以通過URL的方式攜帶一些數(shù)據(jù)去調(diào)用B所提供的接口然后返回相應(yīng)的結(jié)果數(shù)據(jù)。這種方式我們也可以認為是RPC的一種實現(xiàn)方式。對于這種方式我們可以認為是平臺獨立的、語言獨立的,也就是語言中立、平臺中立。也就是我們可以用Python編寫的客戶端去調(diào)用Java編寫的服務(wù)端,因為都是通過URL的方式調(diào)用。因為URL相當(dāng)于契約,URL背后的代碼調(diào)用者無需關(guān)心。
RPC框架調(diào)用基本模型:如person.getPersonByName(String name),首先客戶端先序列化調(diào)用數(shù)據(jù),傳給服務(wù)端,服務(wù)端再反序列化提取調(diào)用信息,查詢客戶端所需要的數(shù)據(jù),完成之后再序列化結(jié)果傳回給客戶端。客戶端再反序列化得到結(jié)果。
Apache thrift是一個可伸縮的,并且跨語言的一種服務(wù)性的開發(fā),他所完成的功能實際上和protobuf是類似的。簡單來說,是Facebook公布的一款開源跨語言的RPC框架。
【什么是RPC框架?】
RPC全稱為Remote Procedure Call,意為遠程過程調(diào)用。
假設(shè)有兩臺服務(wù)器A,B.A服務(wù)器上部署著一個應(yīng)用a,B服務(wù)器上部署著一個應(yīng)用b,現(xiàn)在a希望能夠調(diào)用b應(yīng)用的某個函數(shù)(方法),但是二者不在同一個進程內(nèi),不能直接調(diào)用,就需要通過網(wǎng)絡(luò)傳輸,在AB服務(wù)器之間建一條網(wǎng)絡(luò)傳輸通道,a把參數(shù)傳過去,b接收到參數(shù)調(diào)用自己的方法得到結(jié)果,再通過網(wǎng)絡(luò)傳回給a。
簡單講就是A通過網(wǎng)絡(luò)來調(diào)用B的過程,這個過程要涉及的東西很多,比如多線程、Socket、序列化反序列化、網(wǎng)絡(luò)I/O,很復(fù)雜。于是牛掰的程序員把這些封裝起來做成一套框架供大家使用,就是RPC框架。

【Thrift的跨語言特型】
thrift通過一個中間語言IDL(接口定義語言)來定義RPC的數(shù)據(jù)類型和接口,這些內(nèi)容寫在以.thrift結(jié)尾的文件中,然后通過特殊的編譯器來生成不同語言的代碼,以滿足不同需要的開發(fā)者。比如java開發(fā)者,就可以生成java代碼,c++開發(fā)者可以生成c++代碼,生成的代碼中不但包含目標(biāo)語言的接口定義、方法、數(shù)據(jù)類型,還包含有RPC協(xié)議層和傳輸層的實現(xiàn)代碼。
【Thrift的協(xié)議棧結(jié)構(gòu)】


Thrift是一種c/s的架構(gòu)體系。TServer主要任務(wù)是高效的接受客戶端請求,并將請求轉(zhuǎn)發(fā)給Processor處理。
- 最上層是用戶自行實現(xiàn)的業(yè)務(wù)邏輯代碼;
- Processor是由thrift編譯器自動生成的代碼,它封裝了從輸入數(shù)據(jù)流中讀數(shù)據(jù)和向數(shù)據(jù)流中寫數(shù)據(jù)的操作,它的主要工作是:從連接中讀取數(shù)據(jù),把處理交給用戶實現(xiàn)impl,最后把結(jié)果寫到連接上。
- TProtocol是用于數(shù)據(jù)類型解析的,將結(jié)構(gòu)化數(shù)據(jù)轉(zhuǎn)化為字節(jié)流給TTransport進行傳輸。從TProtocol以下部分是thirft的傳輸協(xié)議和底層I/O通信。
- TTransport是與底層數(shù)據(jù)傳輸密切相關(guān)的傳輸層,負責(zé)以字節(jié)流方式接收和發(fā)送消息體,不關(guān)注是什么數(shù)據(jù)類型。
- 底層IO負責(zé)實際的數(shù)據(jù)傳輸,包括socket、文件和壓縮數(shù)據(jù)流等。
進入Thrift大門的第一個java小實例
- 創(chuàng)建一個服務(wù)Hello,創(chuàng)建文件Hello.thrift,代碼如下:
namespace java service.demo
service Hello{
string helloString(1:string para)
}
- 終端進入Hello.thrift所在目錄,執(zhí)行命令:
thrift -r -gen java Hello.thrift
發(fā)現(xiàn)在當(dāng)前目錄下多了一個gen-java的目錄,里面的有一個Hello.java的文件。這個java文件包含Hello服務(wù)的接口定義Hello.Iface,以及服務(wù)調(diào)用的底層通信細節(jié),包括客戶端的調(diào)用邏輯Hello.Client以及服務(wù)端的處理邏輯Hello.Processor。
- 創(chuàng)建一個Maven管理的Java項目,pom.xml中添加相關(guān)的依賴,并將Hello.java文件復(fù)制到項目中:
<dependency>
<groupId>org.apache.thrift</groupId>
<artifactId>libthrift</artifactId>
<version>0.10.0</version>
</dependency>
<dependency>
<groupId>org.slf4j</groupId>
<artifactId>slf4j-log4j12</artifactId>
<version>1.7.5</version>
</dependency>
- 創(chuàng)建HelloServiceImpl實現(xiàn)Hello.Iface接口:
package service.demo;
import org.apache.thrift.TException;
public class HelloServiceImpl implements Hello.Iface {
public String helloString(String para) throws TException {
return "result:"+para;
}
}
- 創(chuàng)建服務(wù)端實現(xiàn)代碼HelloServiceServer,把HelloServiceImpl作為一個具體的處理器傳遞給Thrift服務(wù)器:
public class HelloServiceServer {
/**
* 啟動thrift服務(wù)器
*/
public static void main(String[] args) {
try {
System.out.println("服務(wù)端開啟....");
// 1.創(chuàng)建TProcessor
TProcessor tprocessor = new Hello.Processor<Hello.Iface>(new HelloServiceImpl());
// 2.創(chuàng)建TserverTransport
TServerSocket serverTransport = new TServerSocket(9898);
// 3.創(chuàng)建TProtocol
TBinaryProtocol.Factory factory = new TBinaryProtocol.Factory();
TServer.Args tArgs = new TServer.Args(serverTransport);
tArgs.processor(tprocessor);
tArgs.protocolFactory(factory);
// 4.創(chuàng)建Tserver,傳入需要的參數(shù),server將以上內(nèi)容集成在一起
TServer server = new TSimpleServer(tArgs);
// 5.啟動server
server.serve();
}catch (TTransportException e) {
e.printStackTrace();
}
}
}
- 創(chuàng)建客戶端實現(xiàn)代碼HelloServiceClient,調(diào)用Hello.client訪問服務(wù)端的邏輯實現(xiàn):
public class HelloServiceClient {
public static void main(String[] args) {
System.out.println("客戶端啟動....");
TTransport transport = null;
try {
transport = new TSocket("localhost", 9898, 30000);
// 協(xié)議要和服務(wù)端一致
TProtocol protocol = new TBinaryProtocol(transport);
Hello.Client client = new Hello.Client(protocol);
transport.open();
String result = client.helloString("哈哈");
System.out.println(result);
} catch (TTransportException e) {
e.printStackTrace();
} catch (TException e) {
e.printStackTrace();
} finally {
if (null != transport) {
transport.close();
}
}
}
}
全部工作完成后,下面來測試一下,先執(zhí)行服務(wù)端main方法,在執(zhí)行客戶端main方法,會在客戶端控制臺打印出:哈哈。