一、概述
Apache Thrift 是 Facebook 實(shí)現(xiàn)的一種高效的、支持多種編程語(yǔ)言的遠(yuǎn)程服務(wù)調(diào)用的框架。Thrift是由Facebook開發(fā)的,并在2008年捐給了Apache基金會(huì),成為了一個(gè)孵化器項(xiàng)目。
Thrift 主要用于各個(gè)服務(wù)之間的RPC通信,支持跨語(yǔ)言,常用的語(yǔ)言比如C++,Java,Python,PHP,Ruby,Erlang,Perl,Haskell,C#,Cocoa,JavaScript,Node.js,Smalltalk,and OCaml都支持。
Thrift是一個(gè)典型的CS(客戶端/服務(wù)端)結(jié)構(gòu),客戶端和服務(wù)端可以使用不同的語(yǔ)言開發(fā),既然客戶端和服務(wù)端能使用不同的語(yǔ)言開發(fā),
那么一定就要有一種中間語(yǔ)言來(lái)聯(lián)系客戶端和服務(wù)端的語(yǔ)言,這種語(yǔ)言就是IDL(Interface Description Language)
Thrift是一個(gè)軟件框架,用來(lái)進(jìn)行可擴(kuò)展且跨語(yǔ)言的服務(wù)的開發(fā)。它結(jié)合了功能強(qiáng)大的軟件堆棧和代碼生成引擎,Thrift是一個(gè)驅(qū)動(dòng)層接口,它提供了用于客戶端使用多種語(yǔ)言實(shí)現(xiàn)的API。
Thrift是個(gè)代碼生成庫(kù),支持的客戶端語(yǔ)言包括C++, Java, Python, PHP, Ruby, Erlang, Perl, Haskell, C#, Cocoa, JavaScript, Node.js, Smalltalk, and OCaml 。它的目標(biāo)是為了各種流行的語(yǔ)言提供便利的RPC調(diào)用機(jī)制,而不需要使用那些開銷巨大的方式,比如SOAP。
二、Thrift的特性
1、語(yǔ)言無(wú)關(guān)的類型
因?yàn)轭愋褪鞘褂枚x文件按照語(yǔ)言中立的方式規(guī)定的,所以它們可以被不同的語(yǔ)言分析。比如,C++的結(jié)構(gòu)可以和Python的字典類型相互交換數(shù)據(jù)。
2、通用傳輸接口
不論你使用的是磁盤文件、內(nèi)存數(shù)據(jù)還是socket流,都可以使用同一段應(yīng)用代碼。
3、協(xié)議無(wú)關(guān)
Thrift會(huì)對(duì)數(shù)據(jù)類型進(jìn)行編碼和解碼,可以跨協(xié)議使用。
4、支持版本
數(shù)據(jù)類型可以加入版本信息,來(lái)支持客戶端API的更新。
三、Thrift的數(shù)據(jù)類型
Thrift 腳本可定義的數(shù)據(jù)類型包括以下幾種類型:
基本類型:
bool: 布爾值
byte: 8位有符號(hào)整數(shù)
i16: 16位有符號(hào)整數(shù)
i32: 32位有符號(hào)整數(shù)
i64: 64位有符號(hào)整數(shù)
double: 64位浮點(diǎn)數(shù)
string: UTF-8編碼的字符串
binary: 二進(jìn)制串
結(jié)構(gòu)體類型:
struct是Thrift IDL中的基本組成塊,由域組成,每個(gè)域有唯一整數(shù)標(biāo)識(shí)符,類型,名字和可選的缺省參數(shù)組成。如定義一個(gè)類似于Twitter服務(wù):
struct Tweet { 1: required i32 userId; // (1) 2: required string userName; // (2) 3: required string text; 4: optional Location loc; // (3) 16: optional string language = "english" // (4) } struct Location { // (5) 1: required double latitude; 2: required double longitude; }
容器類型:
list: 有序元素列表
set: 無(wú)序無(wú)重復(fù)元素集合
map: 有序的key/value集合
異常類型:
exception: 異常類型
服務(wù)類型:
service: 具體對(duì)應(yīng)服務(wù)的類
Thrift的協(xié)議
Thrift可以讓用戶選擇客戶端與服務(wù)端之間傳輸通信協(xié)議的類別,在傳輸協(xié)議上總體劃分為文本(text)和二進(jìn)制(binary)傳輸協(xié)議。為節(jié)約帶寬,提高傳輸效率,一般情況下使用二進(jìn)制類型的傳輸協(xié)議為多數(shù),有時(shí)還會(huì)使用基于文本類型的協(xié)議,這需要根據(jù)項(xiàng)目/產(chǎn)品中的實(shí)際需求。常用協(xié)議有以下幾種:
TBinaryProtocol:二進(jìn)制編碼格式進(jìn)行數(shù)據(jù)傳輸
TCompactProtocol:高效率的、密集的二進(jìn)制編碼格式進(jìn)行數(shù)據(jù)傳輸
TJSONProtocol: 使用JSON文本的數(shù)據(jù)編碼協(xié)議進(jìn)行數(shù)據(jù)傳輸
TSimpleJSONProtocol:只提供JSON只寫的協(xié)議,適用于通過腳本語(yǔ)言解析
Thrift的傳輸層
常用的傳輸層有以下幾種:
TSocket:使用阻塞式I/O進(jìn)行傳輸,是最常見的模式
TNonblockingTransport:使用非阻塞方式,用于構(gòu)建異步客戶端
TFramedTransport:使用非阻塞方式,按塊的大小進(jìn)行傳輸,類似于Java中的NIO
Thrift的服務(wù)端類型
TSimpleServer:?jiǎn)尉€程服務(wù)器端,使用標(biāo)準(zhǔn)的阻塞式I/O
TThreadPoolServer:多線程服務(wù)器端,使用標(biāo)準(zhǔn)的阻塞式I/O
TNonblockingServer:?jiǎn)尉€程服務(wù)器端,使用非阻塞式I/O
THsHaServer:半同步半異步服務(wù)器端,基于非阻塞式IO讀寫和多線程工作任務(wù)處理
TThreadedSelectorServer:多線程選擇器服務(wù)器端,對(duì)THsHaServer在異步IO模型上進(jìn)行增強(qiáng)
四、Thrift的工作原理
定義thrift的文件,由thrift文件(IDL)生成雙方語(yǔ)言的接口、model,在生成的model以及接口中會(huì)有解碼編碼的代碼
五、Thrift案例解析
第一步:打開idea編寫data.thrift文件
namespace java thrift.generated typedef i16 short typedef i32 int typedef i64 long typedef bool boolean typedef string String struct Person{ 1: optional String username, 2: optional int age, 3: optional boolean married } exception DataException{ 1: optional String message, 2: optional String callStack, 3: optional String date } service PersonService{ Person getPersonByUsername(1: required String username) throws(1: DataException dataException), void savePerson(1: required Person person) throws (1: DataException dataException) }
第二步:打開ides設(shè)置File——Setting——Tools——Terminal——Shell path中為cmd.exe路徑,一般為C:\WINDOWS\system32目錄下

第三步:打開終端,輸入命令:thrift --gen java src/thrift/data.thrift 生成java文件

第四步:將生成java文件拷貝到項(xiàng)目中,并修改build.gradle文件增加對(duì)thrift的引用

第五步:編寫實(shí)現(xiàn)類PersonServiceImpl
package com.ssy.netty.thrift; import generated.DataException; import generated.Person; import generated.PersonService; import org.apache.thrift.TException; public class PersonServiceImpl implements PersonService.Iface { @Override public Person getPersonByUsername(String username) throws DataException, TException { System.out.println("Get client param:"+username); Person person = new Person(); person.setUsername(username); person.setAge(20); person.setMarried(false); return person; } @Override public void savePerson(Person person) throws DataException, TException { System.out.println("Get client param:"); System.out.println(person.getUsername()); System.out.println(person.getAge()); System.out.println(person.isMarried()); } }
第六步:編寫服務(wù)端程序ThriftServer,這里我們服務(wù)端和客戶端都采用java編寫
package com.ssy.netty.thrift; import generated.PersonService; import org.apache.thrift.TProcessorFactory; import org.apache.thrift.protocol.TCompactProtocol; import org.apache.thrift.server.THsHaServer; import org.apache.thrift.server.TServer; import org.apache.thrift.transport.TFramedTransport; import org.apache.thrift.transport.TNonblockingServerSocket; public class ThriftServer { public static void main(String[] args) throws Exception{ TNonblockingServerSocket socket = new TNonblockingServerSocket(8899); THsHaServer.Args arg = new THsHaServer.Args(socket).minWorkerThreads(2).maxWorkerThreads(4); PersonService.Processor<PersonServiceImpl> processor = new PersonService.Processor<>(new PersonServiceImpl()); arg.protocolFactory(new TCompactProtocol.Factory()); arg.transportFactory(new TFramedTransport.Factory()); arg.processorFactory(new TProcessorFactory(processor)); TServer server = new THsHaServer(arg); System.out.println("Thrift Server Started!"); server.serve(); } }
第七步:編寫客戶端ThriftClient
package com.ssy.netty.thrift; import generated.Person; import generated.PersonService; import org.apache.thrift.protocol.TCompactProtocol; import org.apache.thrift.protocol.TProtocol; import org.apache.thrift.transport.TFramedTransport; import org.apache.thrift.transport.TSocket; import org.apache.thrift.transport.TTransport; public class ThriftClient { public static void main(String[] args) { TTransport transport = new TFramedTransport(new TSocket("localhost",8899),600); TProtocol protocol = new TCompactProtocol(transport); PersonService.Client client = new PersonService.Client(protocol); try { transport.open(); Person person = client.getPersonByUsername("張三"); System.out.println(person.getUsername()); System.out.println(person.getAge()); System.out.println(person.isMarried()); System.out.println("---------------------------"); Person person2 = new Person(); person2.setUsername("李四"); person2.setAge(30); person2.setMarried(true); client.savePerson(person2); }catch (Exception e){ throw new RuntimeException(e.getMessage(),e); }finally { transport.close(); } } }
第八步:分別運(yùn)行服務(wù)端和客戶端查看效果

通過本節(jié)案例,我們通過java編寫的服務(wù)端和客戶端實(shí)現(xiàn)了基于Thrift的通信,下節(jié)我們分別用java和python來(lái)實(shí)現(xiàn)同樣的功能。
浙公網(wǎng)安備 33010602011771號(hào)