RPC框架 之 Apach thrift
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同步处理(常用方式)