Thrift
1,Apache Thrift 主要用於各個服務之間的RPC通信,支持跨語言,常用語言:C++, Java, Python, PHP, Ruby, Erlang, Perl, Haskell, C#, Cocoa, JavaScript, Node.js, Smalltalk, OCaml and Delphi都支持。
2,Thrift 是一個典型的CS(客戶端/服務器)結構,客戶端和服務端可以使用不同的語言開發,既然客戶端和服務端都能使用不同的語言開發,那麼一定就要有一種中間語言來關聯客戶端和服務端的語言,這種語言就是IDL(Interface Description Language), .thrift 就是一種IDL語言
Trift 數據類型(Base Types)
Trift 不支持無符號類型,因爲很多編程語言不存在無符號類型,比如java,php
基礎數據類型
byte: 有符號字節。
i16: 16位有符號整數。
i32:32位有符號整數。
i64:64位有符號整數。
double: 64位 浮點數。
string: 字符串。
1:optional string username,
2:optional i32 age,
特殊數據類型
binary:一系列未編碼的字節
N.B.。
Structs結構體
Thrift structs 定義了公共對象,也就是oop語言中的常說的類,但是他不具有繼承性.結構體可以包含很多字段,字段包含的內容: numeric field IDs, optional default values, 結構體的目的就是將一些數據聚合在一起,方便傳輸管理
struct Person {
1:optional string username,
2:optional i32 age,
3:optional bool married
}
容器類型(Containers)
list: 一系列由T類型的數據組成的有序列表,元素可以重複。
set: 一系列由T類型的數據組成的無序集合,元素不可重複。
map: 一個字典結構,key爲K 類型,value爲V 類型,相當於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,規則與struct一樣,
exception NotFoundException {
}
exception InvalidRequestException {
1: required string why
}
exception DataException{
1:optional string message;
2:optional string callStack,
3:optional string date
}
服務(Services)
Thrift 定義服務相當於java中創建Interface一樣,創建的service經過代碼生成命令之後就會生成客戶端和服務器端的框架代碼。
- 一個服務(Services)可以定義多個函數。
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定義,相當於改了別名,語法:
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 關鍵字,語法:
const FieldType Identifier = ConstValue
const i32 INT32CONSTANT = 9853
const map<string,string> MAPCONSTANT = {'hello':'world', 'goodnight':'moon'}
命名空間
Thirft的命名空間相當於java中package的意思,主要目的是組織代碼。thrift使用namespace定義命名空間:
namespace 語言名 路徑
namespace java thrift.generated
namespace php tutorial
文件包含
Trift也支持文件包含,相當於php/c/c++中的include,java中的import.使用關鍵字include定義:
include “文件名”
include "paratent.thrift"
註釋
Trifit註釋方式支持shell風格的註釋,支持c/c++ 風格的註釋,即#和//開頭的語句都當做註釋,/**/包含的語句就是註釋。
可選與必選
thrift 提供兩個關鍵字required,option,分別用於表示對應的字段是必填的還是可選的。默認是可選
struct People{
1: required string name;
2: optional i32 age;
}
一個完整的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 工作原理
如何實現多語言之間的通信?
1,數據傳輸使用socket(多種語言均支持),數據再以特定的格式發送,接收語言進行解析。
2,定義thrift 的文件,由thrift文件(IDL)生成雙方語言的接口,model,在生成的model以及接口中會有解碼編碼的代碼。
安裝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
實現service 接口方法 UserServiceIml.java
public class UserServiceIml implements UserService.Iface {
@Override
public User login(UserRequest userRequst) throws DataException, TException {
System.out.println("--------服務器: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("--------服務器:logOut--------------");
System.out.println(userRequst);
}
@Override
public List<User> userList() throws DataException, TException {
System.out.println("--------服務器:userList--------------");
List userList = new ArrayList<>();
User user = new User();
user.setId(2);
user.setUsername("我會回來的");
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;
}
}
服務端 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());
//協議 二進制壓縮
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服務器端代碼
創建Handler,用於處理業務邏輯,數據處理接口.
基於Handler創建Processor,數據處理對象
創建Transport(通信方式),數據傳輸方式
創建Protocol方式(設定傳輸格式),數據傳輸協議
基於Processor, Transport和Protocol創建Server
運行Server
thrift客戶端:
創建Transport
創建Protocol方式
基於Transport和Protocol創建Client
運行Client的方法
Thrift 深入瞭解
Thrift 架構圖
client
表示與服務端連接的那個對象,進行方法調用,
write/read
thift 幫助我們自動生成的, write 是將客戶端的數據寫到socket(服務器端),read 從socket讀到客戶端
TProtocal
應用層 表示協議 數據格式 比如json
TTransport
傳輸層
TCompactProtocol 即使使用charlse 抓包 拿到數據也是二進制
Thrift 傳輸格式Protocol
TBinaryProtocol 二進制格式
TCompactProtocol 壓縮二進制格式(常用的方式)
TJSONProtocol JSON格式
TSimpleJSONProtocal 提供json只寫協議,生成的文件很容易通過腳本語言解析。
TDebugProtocal 使用易懂的可讀的文本格式,以便於debug
Thrift 傳輸方式Transport
TSocket 阻塞式socket
TFramedTransport 以frame 爲單位進行傳輸,非阻塞式服務中使用(常用的方式)
TFileTransport 以文件形式進行傳輸
TMemoryTransport 將內存用於I/O , java 實現時內部實際使用了簡單的ByteArrayOutputStream。
TZlibTransport 使用zlib進行壓縮,與其他傳輸方式聯合使用。當前無java實現。
Thrift 支持的服務模型
TSimpleServer 簡單的單線程服務模型,常用於測試
TThreadPoolServer 多線程服務模型,使用標準的阻塞式IO
TNonbolockingServer - 多線程服務模型,使用非阻塞式IO(需使用TFramedTransport數據傳輸方式)
THsHaServer - THsHa 引入了線程池去處理,其模型把讀寫任務放倒線程池去處理;
Half-sync/Half-async的處理模式,Half-async是在處理IO事件上(accept/read/write io),
Half-sync用於handler對rpc同步處理(常用方式)