Apache Thrift 安裝及快速入門
Apache Thrift是什麼?
The Apache Thrift software framework, for scalable cross-language services development, combines a software stack with a code generation engine to build services that work efficiently and seamlessly between C++, Java, Python, PHP, Ruby, Erlang, Perl, Haskell, C#, Cocoa, JavaScript, Node.js, Smalltalk, OCaml and Delphi and other languages.
Apache Thrift軟體框架用於可擴充套件的跨語言服務開發,將軟體堆疊與程式碼生成引擎相結合,構建可在C ++,Java,Python,PHP,Ruby,Erlang,Perl,Haskell,C#之間高效無縫工作的服務, Cocoa,JavaScript,Node.js,Smalltalk,OCaml和Delphi等語言。
Thrift最初由facebook研發,主要用於各個服務之間的RPC通訊,支援跨語言,支援的語言有C++,Java,Python,PHP,Ruby,Erlang,PErl,Haskell,C#,Cocoa,JavaScript,Node.js,
Smalltalk,and OCaml都支援。
Thrift是一個典型的CS(客戶端/服務端)結構,客戶端和服務端可以使用不同的語言開發。既然客戶端和伺服器端能使用不同的語言開發,那麼一定就要有一種中間語言來關聯客戶端和伺服器端的語言。這種語言就是IDL(Interface Description Language)。
Thrift不支援無符號型別,因為很多程式語言不存在無符號型別,比如說java。一個RPC框架如果支援多種語言,那麼這個RPC框架所支援的資料型別一定是這個RPC框架多語言支援的資料型別的交集。
Apache Thrift的一些概念
Thrift支援的資料型別
bool: 布林型別(true或者false)
byte: 有符號位元組
i16: 16位有符號整數
i32: 32位有符號整數
i64: 64位有符號整數
double: 64位浮點數
string: 字串
集合中的元素可以是除了service之外的任何型別,包括exception。這邊的service和exception是Thrift支援的元件,Thrift支援三種元件,分別是Structs(結構體),Service(客戶端和服務端通訊的介面),exception(客戶端和服務端通訊介面丟擲的異常)
結構體(struct)
就像C語言一樣,Thrift支援struct型別,目的就是將一些資料聚合在一起,方便傳輸管理,struct的定義形式如下:
struct People{
1:string name;
2:i32 age;
3:string gender;
}
列舉(enum)
列舉的定義形式和Java的Enum定義類似
enum Gender{
MALE,
FEMALE
}
異常(exception)
Thrift支援自定義exception,規則與struct一樣
exception RequestException{
1: i32 code;
2: string reason;
}
服務(service)
Thrift定義服務相當於Java中建立Interface一樣,建立的service經過程式碼生成命令之後就會生成客戶端和伺服器端的框架程式碼。定義形式如下:
service HelloWorldService{
//service中定義的函式,相當於Java Interface中定義的方法
string doAction(1:string name,2:i32 age);
}
型別定義
Thrift支援類似C++一樣的typedef定義,比如我們對i32不熟悉,我們就使用int類代替i32,比如我們對i64不熟悉,我們就使用long代替i64
typedef i32 int
typedef i64 long
常量(const)
Thrift也支援常量定義,使用const關鍵字
const i32 MIN_GATE=30
const string MY_WEBSITE="http://facebook.com"
名稱空間
Thrift的名稱空間相當於java中的package的意思,主要目的是組織程式碼。Thift使用關鍵字namespave定義名稱空間:
namespace java com.test.thift.demo
格式是:namespace 語言名 路徑
檔案包含
Thrift也支援檔案包含,相當於C/C++中的include,java中的import。使用關鍵字include定義:
include "global.thift"
註釋
Thrift註釋方式支援shell風格的註釋,支援C/C++風格的註釋,即#和開頭的語句都當作註釋,/**/包裹的語句也是註釋。
可選與必選
Thrift提供兩個關鍵字required,optional,分別用於表示對應的欄位是必填的還是可選的
struct People{
1:required string name;
2:optional i32 age;
}
Thrift傳輸格式(協議)
- TBinaryProtocal-二進位制格式
- TCompactProtocol-壓縮格式
- TJSONProtocol-JSON格式
- TSimpleJSONProtocol-提供JSON只寫協議,生成的檔案很容易通過指令碼語言解析
- TDebugProtocol-使用易懂的可讀文字格式,以便於debug
Thrift資料傳輸方式
- TSocket-阻塞式socket
- TFramedTransport-以frame為單位進行傳輸,非阻塞式服務中使用
- TFileTransport-以檔案形式進行傳輸
- TMemoryInputTransport-將記憶體用語I/O,Java實現時內部實際使用了簡單的ByteArrayOutputStream。
- TZlibTransport-使用zlib進行壓縮,與其他傳輸方式聯合使用。當前無java實現。
Thrift支援的服務模型
- TThreadPoolServer - 簡單的單執行緒服務模型,常用於測試
- TSimpleServer - 多執行緒服務模型,使用標準的阻塞式IO
- TNonblockingServer - 多執行緒服務模型,使用非阻塞式IO(需要使用TFramedTransport資料傳輸方式)
- THsHaServer-THsHa引入了執行緒池去處理,其模型把讀寫任務放到執行緒池處理;Half-sync/Half-async的處理模式,Half-async是在處理IO事件上
(accept/read/write io),Half-sync用於handler對rpc的同步處理。
注意
一般在工作中使用TCompactProtocol傳輸協議,使用TFramedTransport資料傳輸方式,使用THsHaServer服務模型。
Thrift支援的容器型別
list:一系列由T型別的資料組成的有序列表,元素可以重複。
set:一系列由T型別的資料組成的無序集合,元素不可重複。
map:一個字典結構,key為k型別,value為V型別,相當於java中的HashMap
以上集合容器都可以使用泛型的。
Thrift工作原理
如何實現多語言之間的通訊?
資料傳輸實現socket(多種語言均支援),資料再以特定的格式(String等)傳送,接收方語言進行解析。
Apache Thrift定義的thrift的檔案(IDL),由thrift檔案(IDL)生成雙方語言的介面,model,在生成的model以及介面中會有解碼編碼的程式碼。
Thrift 架構
Thrift架構圖Thrift的安裝
官方網站提供的下載安裝地址,根據不同的作業系統選擇自己的安裝方式
mac電腦推薦使用更簡單的安裝方式Homebrew工具,
Homebrew官方網址
先安裝Homebrew:
/usr/bin/ruby -e "$(curl -fsSL https://raw.githubusercontent.com/Homebrew/install/master/install)"
安裝成功之後,檢測Homebrew:
➜ ~ which brew
/usr/local/bin/brew
安裝Apache Thrift
➜ ~ brew install thrift
安裝完成之後:
檢視具體安裝資訊:
➜ ~ which thrift
/usr/local/bin/thrift
➜ ~ thrift --version
Thrift version 0.10.0
thrift --help
快速入門
定義idl檔案
先定義一個idl檔案(介面描述檔案),定義了結構體(struct),異常(exception)和服務(service)
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 dateException),
void savePerson(1: required Person person) throws (1: DataException dataException)
}
使用thrift編譯器生成編譯檔案
thrift --gen java src/thrift/data.thrift
java生成的程式碼
將生成的程式碼複製到src/main目錄下,發現報錯,加入java的依賴pom檔案:
<dependency>
<groupId>org.apache.thrift</groupId>
<artifactId>libthrift</artifactId>
<version>0.10.0</version>
</dependency>
使用java編寫客戶端與伺服器端的程式碼
編寫介面實現類,實際開發中放在服務端
import org.apache.thrift.TException;
import thrift.generated.DataException;
import thrift.generated.Person;
import thrift.generated.PersonService;
public class PersonServiceImpl implements PersonService.Iface{
@Override
public Person getPersonByUsername(String username) throws DataException, TException {
System.out.println("Got client Param:" + username);
Person person = new Person();
person.setUsername(username);
person.setAge(32);
person.setMarried(true);
return person;
}
@Override
public void savePerson(Person person) throws DataException, TException {
System.out.println("Got Client Param: ");
System.out.println(person.getUsername());
System.out.println(person.getAge());
System.out.println(person.isMarried());
}
}
伺服器端程式碼:
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;
import thrift.generated.PersonService;
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));
//半同步半非同步的server
TServer server = new THsHaServer(arg);
System.out.println("Thrift Server started!");
//死迴圈,永遠不會退出
server.serve();
}
}
客戶端程式碼:
import org.apache.thrift.protocol.TCompactProtocol;
import org.apache.thrift.protocol.TProtocol;
import org.apache.thrift.transport.TFastFramedTransport;
import org.apache.thrift.transport.TSocket;
import org.apache.thrift.transport.TTransport;
import thrift.generated.Person;
import thrift.generated.PersonService;
//服務端的協議和客戶端的協議要一致
public class ThriftClient {
public static void main(String[] args) {
TTransport tTransport = new TFastFramedTransport(new TSocket("localhost",8899),600);
TProtocol tProtocol = new TCompactProtocol(tTransport);
PersonService.Client client = new PersonService.Client(tProtocol);
try{
tTransport.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 ex){
throw new RuntimeException(ex.getMessage(),ex);
}finally {
tTransport.close();
}
}
}
啟動伺服器,再啟動客戶端,
客戶端列印:
Received 1
張三
32
true
............
Received 2
伺服器端列印:
Thrift Server started!
Got client Param:張三
Got Client Param:
李四
30
true
跟我們之前的Google Protobuf相比,Google Protobuf只是進行編解碼(序列化與反序列)操作,使用netty作為網路載體,進行遠端方法呼叫。而Thrift不僅僅既可以進行編解碼工作,還提供傳輸物件功能,並且可以自己定義業務介面。
作者:二月_春風
連結:https://www.jianshu.com/p/bfcba5f0eaa1
來源:簡書
著作權歸作者所有。商業轉載請聯絡作者獲得授權,非商業轉載請註明出處。