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同步处理(常用方式)