1、概述java
Apache Thrift 是 Facebook 實現的一種高效的、支持多種編程語言的遠程服務調用的框架。Thrift是由Facebook開發的,並在2008年捐給了Apache基金會,成爲了一個孵化器項目。python
Thrift 主要用於各個服務之間的RPC通訊,支持跨語言,經常使用的語言好比C++,Java,Python,PHP,Ruby,Erlang,Perl,Haskell,C#,Cocoa,JavaScript,Node.js,Smalltalk,and OCaml都支持。
Thrift是一個典型的CS(客戶端/服務端)結構,客戶端和服務端可使用不一樣的語言開發,既然客戶端和服務端能使用不一樣的語言開發,
那麼必定就要有一種中間語言來聯繫客戶端和服務端的語言,這種語言就是IDL(Interface Description Language)git
Thrift是一個軟件框架,用來進行可擴展且跨語言的服務的開發。它結合了功能強大的軟件堆棧和代碼生成引擎,Thrift是一個驅動層接口,它提供了用於客戶端使用多種語言實現的API。apache
Thrift是個代碼生成庫,支持的客戶端語言包括C++, Java, Python, PHP, Ruby, Erlang, Perl, Haskell, C#, Cocoa, JavaScript, Node.js, Smalltalk, and OCaml 。它的目標是爲了各類流行的語言提供便利的RPC調用機制,而不須要使用那些開銷巨大的方式,好比SOAP。編程
2、Thrift的特性
一、語言無關的類型
由於類型是使用定義文件按照語言中立的方式規定的,因此它們能夠被不一樣的語言分析。好比,C++的結構能夠和Python的字典類型相互交換數據。
二、通用傳輸接口
不論你使用的是磁盤文件、內存數據仍是socket流,均可以使用同一段應用代碼。
三、協議無關
Thrift會對數據類型進行編碼和解碼,能夠跨協議使用。
四、支持版本
數據類型能夠加入版本信息,來支持客戶端API的更新。數組
Thrift 腳本可定義的數據類型包括如下幾種類型:服務器
基本類型:
bool: 布爾值
byte: 8位有符號整數
i16: 16位有符號整數
i32: 32位有符號整數
i64: 64位有符號整數
double: 64位浮點數
string: UTF-8編碼的字符串
binary: 二進制串
結構體類型:
struct是Thrift IDL中的基本組成塊,由域組成,每一個域有惟一整數標識符,類型,名字和可選的缺省參數組成。如定義一個相似於Twitter服務:多線程
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: 無序無重複元素集合
map: 有序的key/value集合
異常類型:
exception: 異常類型
服務類型:
service: 具體對應服務的類
Thrift的協議
Thrift可讓用戶選擇客戶端與服務端之間傳輸通訊協議的類別,在傳輸協議上整體劃分爲文本(text)和二進制(binary)傳輸協議。爲節約帶寬,提升傳輸效率,通常狀況下使用二進制類型的傳輸協議爲多數,有時還會使用基於文本類型的協議,這須要根據項目/產品中的實際需求。經常使用協議有如下幾種:異步
TBinaryProtocol:二進制編碼格式進行數據傳輸
TCompactProtocol:高效率的、密集的二進制編碼格式進行數據傳輸
TJSONProtocol: 使用JSON文本的數據編碼協議進行數據傳輸
TSimpleJSONProtocol:只提供JSON只寫的協議,適用於經過腳本語言解析
Thrift的傳輸層
經常使用的傳輸層有如下幾種:
TSocket:使用阻塞式I/O進行傳輸,是最多見的模式
TNonblockingTransport:使用非阻塞方式,用於構建異步客戶端
TFramedTransport:使用非阻塞方式,按塊的大小進行傳輸,相似於Java中的NIO
Thrift的服務端類型
TSimpleServer:單線程服務器端,使用標準的阻塞式I/O
TThreadPoolServer:多線程服務器端,使用標準的阻塞式I/O
TNonblockingServer:單線程服務器端,使用非阻塞式I/O
THsHaServer:半同步半異步服務器端,基於非阻塞式IO讀寫和多線程工做任務處理
TThreadedSelectorServer:多線程選擇器服務器端,對THsHaServer在異步IO模型上進行加強
4、Thrift的工做原理
定義thrift的文件,由thrift文件(IDL)生成雙方語言的接口、model,在生成的model以及接口中會有解碼編碼的代碼
5、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設置File——Setting——Tools——Terminal——Shell path中爲cmd.exe路徑,通常爲C:\WINDOWS\system32目錄下
第三步:打開終端,輸入命令:thrift --gen java src/thrift/data.thrift 生成java文件
第四步:將生成java文件拷貝到項目中,並修改build.gradle文件增長對thrift的引用
第五步:編寫實現類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()); } }
第六步:編寫服務端程序ThriftServer,這裏咱們服務端和客戶端都採用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(); } } }
第八步:分別運行服務端和客戶端查看效果
經過本節案例,咱們經過java編寫的服務端和客戶端實現了基於Thrift的通訊,下節咱們分別用java和python來實現一樣的功能。