Thrift
1,Apache Thrift 主要用于各個(gè)服務(wù)之間的RPC通信,支持跨語言,常用語言:C++, Java, Python, PHP, Ruby, Erlang, Perl, Haskell, C#, Cocoa, JavaScript, Node.js, Smalltalk, OCaml and Delphi都支持。
2,Thrift 是一個(gè)典型的CS(客戶端/服務(wù)器)結(jié)構(gòu),客戶端和服務(wù)端可以使用不同的語言開發(fā),既然客戶端和服務(wù)端都能使用不同的語言開發(fā),那么一定就要有一種中間語言來關(guān)聯(lián)客戶端和服務(wù)端的語言,這種語言就是IDL(Interface Description Language), .thrift 就是一種IDL語言
Trift 數(shù)據(jù)類型(Base Types)
Trift 不支持無符號(hào)類型,因?yàn)楹芏嗑幊陶Z言不存在無符號(hào)類型,比如java,php
基礎(chǔ)數(shù)據(jù)類型
byte: 有符號(hào)字節(jié)。
i16: 16位有符號(hào)整數(shù)。
i32:32位有符號(hào)整數(shù)。
i64:64位有符號(hào)整數(shù)。
double: 64位 浮點(diǎn)數(shù)。
string: 字符串。
1:optional string username,
2:optional i32 age,
特殊數(shù)據(jù)類型
binary:一系列未編碼的字節(jié)
N.B.。
Structs結(jié)構(gòu)體
Thrift structs 定義了公共對象,也就是oop語言中的常說的類,但是他不具有繼承性.結(jié)構(gòu)體可以包含很多字段,字段包含的內(nèi)容: numeric field IDs, optional default values, 結(jié)構(gòu)體的目的就是將一些數(shù)據(jù)聚合在一起,方便傳輸管理
struct Person {
1:optional string username,
2:optional i32 age,
3:optional bool married
}
容器類型(Containers)
list: 一系列由T類型的數(shù)據(jù)組成的有序列表,元素可以重復(fù)。
set: 一系列由T類型的數(shù)據(jù)組成的無序集合,元素不可重復(fù)。
map: 一個(gè)字典結(jié)構(gòu),key為K 類型,value為V 類型,相當(dāng)于java中的HashMap.
1: list<string> strings,
2: list<i32> newlist,
3: set<i32> newset,
4: set<string> a_set2500,
5: map<i32, i32> newmap,
6: map<string,string> map_field,
異常(Exceptions)
thift支持自定義exception,規(guī)則與struct一樣,
exception NotFoundException {
}
exception InvalidRequestException {
1: required string why
}
exception DataException{
1:optional string message;
2:optional string callStack,
3:optional string date
}
服務(wù)(Services)
Thrift 定義服務(wù)相當(dāng)于java中創(chuàng)建Interface一樣,創(chuàng)建的service經(jīng)過代碼生成命令之后就會(huì)生成客戶端和服務(wù)器端的框架代碼。
- 一個(gè)服務(wù)(Services)可以定義多個(gè)函數(shù)。
service FacebookService {
string getName(),
map<string, i64> getCounters(),
i64 getCounter(1: string key),
}
service PersonService{
Person getPersonByUsername(1:required string username) throws(1:DataException dataException),
void savePerson(1:required Person person) throws(1:DataException dataException)
}
類型定義(typedef)
thirift 支持類似c++ 一樣的typedef定義,相當(dāng)于改了別名,語法:
typedef DefinitionType Identifier
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
}
常量(const)
thrift 也支持常量定義,使用const 關(guān)鍵字,語法:
const FieldType Identifier = ConstValue
const i32 INT32CONSTANT = 9853
const map<string,string> MAPCONSTANT = {'hello':'world', 'goodnight':'moon'}
命名空間
Thirft的命名空間相當(dāng)于java中package的意思,主要目的是組織代碼。thrift使用namespace定義命名空間:
namespace 語言名 路徑
namespace java thrift.generated
namespace php tutorial
文件包含
Trift也支持文件包含,相當(dāng)于php/c/c++中的include,java中的import.使用關(guān)鍵字include定義:
include “文件名”
include "paratent.thrift"
注釋
Trifit注釋方式支持shell風(fēng)格的注釋,支持c/c++ 風(fēng)格的注釋,即#和//開頭的語句都當(dāng)做注釋,/**/包含的語句就是注釋。
可選與必選
thrift 提供兩個(gè)關(guān)鍵字required,option,分別用于表示對應(yīng)的字段是必填的還是可選的。默認(rèn)是可選
struct People{
1: required string name;
2: optional i32 age;
}
一個(gè)完整的thrift 文件
namespace java thrift.generated
namespace php tutorial
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 dataEception),
String testSave(1:required String name)
}
Thrift 工作原理
如何實(shí)現(xiàn)多語言之間的通信?
1,數(shù)據(jù)傳輸使用socket(多種語言均支持),數(shù)據(jù)再以特定的格式發(fā)送,接收語言進(jìn)行解析。
2,定義thrift 的文件,由thrift文件(IDL)生成雙方語言的接口,model,在生成的model以及接口中會(huì)有解碼編碼的代碼。
安裝thrift
brew install thrift
生成代碼
thrift -r --gen java src/thrift/person.thrift
thrift -r --java php src/thrift/person.thrift
或者
thrift -r --java php:server src/thrift/person.thrift
java thrift示例
idl users.thrift
namespace java com.lihao.netty.thrift2
namespace php thrift
typedef i16 short
typedef i32 int
typedef i64 long
typedef bool boolean
typedef string String
struct User{
1: String username,
2: int age,
3: int id,
}
struct UserRequest{
1:int id;
2:String token;
3:String username;
4:String passworld;
}
exception DataException {
1: String message;
2: String callStack;
3: String date
}
service UserService{
User login(1:required UserRequest userRequst) throws(1: DataException dataException);
void logOut(1:required UserRequest userRequst) throws(1: DataException dataException);
list<User> userList() throws(1: DataException dataException);
}
生成代碼
thrift -r --gen java src/thrift/users.thrift
實(shí)現(xiàn)service 接口方法 UserServiceIml.java
public class UserServiceIml implements UserService.Iface {
@Override
public User login(UserRequest userRequst) throws DataException, TException {
System.out.println("--------服務(wù)器:login--------------");
System.out.println(userRequst);
User user = new User();
user.setId(2);
user.setUsername("張三");
user.setAge(20);
return user;
}
@Override
public void logOut(UserRequest userRequst) throws DataException, TException {
System.out.println("--------服務(wù)器:logOut--------------");
System.out.println(userRequst);
}
@Override
public List<User> userList() throws DataException, TException {
System.out.println("--------服務(wù)器:userList--------------");
List userList = new ArrayList<>();
User user = new User();
user.setId(2);
user.setUsername("我會(huì)回來的");
user.setAge(20);
User user1 = new User();
user1.setId(2);
user1.setUsername("張三-2---");
user1.setAge(20);
User user2 = new User();
user2.setId(2);
user2.setUsername("張三---123");
user2.setAge(20);
userList.add(user);
userList.add(user1);
userList.add(user2);
return userList;
}
}
服務(wù)端 Server.java
public class Server {
public static void main(String... args) throws Exception {
/**
* +-------------------------------------------+
| Server |
| (single-threaded, event-driven etc) |
+-------------------------------------------+
| Processor |
| (compiler generated) |
+-------------------------------------------+
| Protocol |
| (JSON, compact etc) |
+-------------------------------------------+
| Transport |
| (raw TCP, HTTP etc) |
+-------------------------------------------+
*/
TNonblockingServerSocket socket = new TNonblockingServerSocket(7788);
THsHaServer.Args arg = new THsHaServer.Args(socket).minWorkerThreads(2).maxWorkerThreads(4);
UserService.Processor<UserServiceIml> processor = new UserService.Processor<>(new UserServiceIml());
//協(xié)議 二進(jìn)制壓縮
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();
}
}
java客戶端 client
public class Client {
public static void main(String ...args) {
TTransport transport = new TFastFramedTransport(new TSocket("localhost",7788),600);
TCompactProtocol protocol = new TCompactProtocol(transport);
UserService.Client client = new UserService.Client(protocol);
try {
transport.open();
UserRequest request = new UserRequest();
request.setUsername("li si");
request.setPassworld("****");
User user = client.login(request);
System.out.println("--------------------------------");
System.out.println(user);
System.out.println("--------------------------------");
List<User> list = client.userList();
System.out.println(list);
System.out.println("--------------------------------");
} catch (Exception ex){
ex.printStackTrace();
} finally {
transport.close();
}
}
}
php客戶端
- 生成php代碼
thrift -r --gen php src/thrift/users.thrift
- 下載thrift php lib包
https://github.com/apache/thrift
UserClient.php
header("Content-Type: text/html; charset=UTF-8");
ini_set("display_errors", 'On');
error_reporting(E_ALL);
require_once __DIR__ . '/lib/Thrift/ClassLoader/ThriftClassLoader.php';
use Thrift\ClassLoader\ThriftClassLoader;
use \Thrift\Transport\TSocket;
use \Thrift\Transport\TFramedTransport;
use \Thrift\Protocol\TCompactProtocol;
use \thrift\UserServiceClient;
$GEN_DIR = __DIR__ . '/gen-php';
$loader = new ThriftClassLoader();
$loader->registerNamespace('Thrift', __DIR__ . '/lib');
$loader->registerDefinition('thrift', $GEN_DIR);
$loader->register();
$socket = new TSocket("localhost", 7788);
$transport = new TFramedTransport($socket);
$protoc = new TCompactProtocol($transport);
$client = new UserServiceClient($protoc);
echo 1;
try {
$transport->open();
echo 2;
$request = new \thrift\UserRequest();
$request->username = "張三";
$user = $client->login($request);
print_r($user);
echo 3;
$list = $client->userList();
echo $list[0]->username."ok";
} catch (\tutorial\DataException $ex) {
echo 'error';
print 'TException: ' . $ex->getMessage() . "\n";
}
$transport->close();

代碼步驟
thfit服務(wù)器端代碼
創(chuàng)建Handler,用于處理業(yè)務(wù)邏輯,數(shù)據(jù)處理接口.
基于Handler創(chuàng)建Processor,數(shù)據(jù)處理對象
創(chuàng)建Transport(通信方式),數(shù)據(jù)傳輸方式
創(chuàng)建Protocol方式(設(shè)定傳輸格式),數(shù)據(jù)傳輸協(xié)議
基于Processor, Transport和Protocol創(chuàng)建Server
運(yùn)行Server
thrift客戶端:
創(chuàng)建Transport
創(chuàng)建Protocol方式
基于Transport和Protocol創(chuàng)建Client
運(yùn)行Client的方法
Thrift 深入了解
Thrift 架構(gòu)圖

client
表示與服務(wù)端連接的那個(gè)對象,進(jìn)行方法調(diào)用,
write/read
thift 幫助我們自動(dòng)生成的, write 是將客戶端的數(shù)據(jù)寫到socket(服務(wù)器端),read 從socket讀到客戶端
TProtocal
應(yīng)用層 表示協(xié)議 數(shù)據(jù)格式 比如json
TTransport
傳輸層
TCompactProtocol 即使使用charlse 抓包 拿到數(shù)據(jù)也是二進(jìn)制
Thrift 傳輸格式Protocol
TBinaryProtocol 二進(jìn)制格式
TCompactProtocol 壓縮二進(jìn)制格式(常用的方式)
TJSONProtocol JSON格式
TSimpleJSONProtocal 提供json只寫協(xié)議,生成的文件很容易通過腳本語言解析。
TDebugProtocal 使用易懂的可讀的文本格式,以便于debug
Thrift 傳輸方式Transport
TSocket 阻塞式socket
TFramedTransport 以frame 為單位進(jìn)行傳輸,非阻塞式服務(wù)中使用(常用的方式)
TFileTransport 以文件形式進(jìn)行傳輸
TMemoryTransport 將內(nèi)存用于I/O , java 實(shí)現(xiàn)時(shí)內(nèi)部實(shí)際使用了簡單的ByteArrayOutputStream。
TZlibTransport 使用zlib進(jìn)行壓縮,與其他傳輸方式聯(lián)合使用。當(dāng)前無java實(shí)現(xiàn)。
Thrift 支持的服務(wù)模型
TSimpleServer 簡單的單線程服務(wù)模型,常用于測試
TThreadPoolServer 多線程服務(wù)模型,使用標(biāo)準(zhǔn)的阻塞式IO
TNonbolockingServer - 多線程服務(wù)模型,使用非阻塞式IO(需使用TFramedTransport數(shù)據(jù)傳輸方式)
THsHaServer - THsHa 引入了線程池去處理,其模型把讀寫任務(wù)放倒線程池去處理;
Half-sync/Half-async的處理模式,Half-async是在處理IO事件上(accept/read/write io),
Half-sync用于handler對rpc同步處理(常用方式)