Thrift
源于Thrift
作为一个开源项目提交给了Apache
基金会。对于当时的Thrift
是为了解决Thrift
可以支持多种程序语言,如C++
、C#
、Cocoa
、Erlang
、Haskell
、Java
、Ocami
、Perl
、PHP
、Python
、Ruby
和Smalltalk
。它被当作一个远程过程调用(RPC)框架来使用。
在多种不同的语言之间通信,Thrift
可以作为高性能的通信中间件使用,它支持数据(对象)序列化和多种类型的 RPC 服务。Thrift
适用于静态的数据交换,需要先确定好它的数据结构,当数据结构发生变化时,必须重新编辑 IDL 文件、生成代码和编译,这一点跟其他 IDL
工具相比可以视为是 Thrift
的弱项。Thrift
适用于搭建大型数据交换及存储的通用工具,对于大型系统中的内部数据传输,相对于 JSON
和 XML
在性能和传输大小上都有明显的优势。
# 一、Thrift
主要由5部分组成
1)、语言系统以及 IDL
编译器:负责由用户给定的 IDL
文件生成相应语言的接口代码;
2)、TProtocol
:RPC
的协议层,可以选择多种不同的对象序列化方法,如 JSON
和 Binary
;
3)、TTransport
:RPC
的传输层,同样可以选择不同的传输层实现,如 Socket
、NIO
、MemoryBuffer
等;
4)、TProcessor
:作为协议层和用户提供的服务实现之间的纽带,负责调用服务实现的接口;
5)、TServer
:聚合TProtocol
、TTransport
和 TProcessor
等对象;
我们重点关注的是编解码框架,与之对应的就是 TProtocol
。由于 Thrift 的 RPC(Remote Procedure Call)
服务调用和编解码框架绑定在一起,所以 ,通常使用 Thrift
的时候会采取 RPC
框架的方式。但是,它的 TProtocol
编解码框架还是可以以类库的方式独立使用的。
与 Protobuf
(Google
的Protobuf
) 比较类似的是,Thrift
通过 IDL
描述接口和数据结构定义,它支持 8种 Java
基本类型、Map
、Set
和List
,支持可选和必须定义,功能非常强大。因为可以定义数据结构中字段的顺序,所以它也可以支持协议的前向兼容。
# 二、Thrift
支持三种典型的编解码方式
■ 通用的二进制编解码;
■ 压缩二进制编解码;
■ TJSONProtocol
:JSON
格式;
■ 优化的可选字段压缩编解码;
由于支持二进制压缩编解码,Thrift
的编解码性能表现也相当优异,远远超过 Java
序列化和 RMI
等。
Thrift
工作原理(实现多语言间的通信):数据的传输使用 socket
(多种语言均支持),数据在以特定的格式(String
等)发送,接收方按对应的格式进行解析。定义 thrift
文件,有 thrift
文件(IDL
)生成双方语言的接口、Model
,在生成接口和 Model
的代码中包含编解码的代码。
# 三、Thrift
数据/容器类型
Thrift
通过中间件语言IDL
(接口定义语言)来定义RPC
的数据类型和接口,这些内容写在以.thrift
结尾的文件中。然后通过特殊的编译器来生成不同语言的代码,以满足不同需求的开发者。比如java
开发者,就可以生成java
代码,c++
开发者可以生成c++
代码,生成的代码中不但包含目标语言的接口定义、方法、数据类型,还包含有RPC
协议层和传输层的实现代码。
Thrift
不支持无符号类型,因为很多语言不存在无符号类型,比如 Java
。
■ byte
:有符号字节;
■ i16
:16位有符号整数;
■ i32
:32位有符号整数;
■ i64
:64位有符号整数;
■ double
:64位浮点数;
■ string
:字符串
容器类型:List
、Set
、Map
:与 Java
中的类型类似。
# 四、Thrift
的协议栈结构
![粘包](../../assets/img/thrift.9ed4b082.png)
Thrift
是一种 C/S
的架构体系。TServer
主要任务是高效的接受客户端请求,并将请求转发给 TProcessor
处理。
■ 最上层是用户自行实现的业务逻辑代码;
■ Processor
是由 thrift
编译器自动生成的代码,它封装了从输入流中读取数据和向数据流中写数据的操作,它的主要工作是:从连接中读取数据,把处理交给用户实现 impl
,最后把结果写入连接;
■ TProtocol
是用于数据类型解析的,将结构化数据转化为字节流给 TTransport
进行传输。从 TProtocol
以下部分是 thirft
的传输协议和底层 I/O
通信;
■ TTransport
是与底层数据传输密切相关的传输层,负责以字节流方式接收和发送消息体,不关注是什么数据类型;
■ 底层 IO 负责实际的数据传输,包括 socket、文件和压缩数据流等;
# 五、Thrift IDL 文件
namespace java com.test.thrift.demo
struct News {
1:i32 id;
2:string titie;
3:string content;
4:string mediaFrom;
5:string autor;
}
service IdenxNewsCreratorServices {
bool indexNews(1:NewsModel indexNews),
bool removeNewsById(1:i32 id)
}
enum Gender {
MALE,
FEMALE
}
exception RequestExce {
1:i32 id;
2:string titie;
}
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
【1】结构体(struct
):就像 C语言一样,Thrift 支持 Struct
类型,目的就是将一些数据聚合在一起方便传输管理。
【2】枚举(enum
):定义形式与 Java 的定义形式类似。
【3】异常(exception
):支持自定义 exception
,规则与 Struct
一致。
【4】服务(service
):Thrift
定义服务相当于 java
中创建 interface
一样,创建的 service
经过代码命名之后就会生成客户端和服务端的框架代码。相当于若干个方法的集合。
【5】类型定义:支持类型C++ 一样的 typedef
定义:typedef
i32
int
,后续的操作就可以使用 int
代替 i32
【6】常量(const
):使用 const
关键字:const i32 MAX_RETRIES_TIME = 10
【7】命名空间:相当于 java
中的 package
的意思,主要的目的是组织代码。thrift
的关键字 namespace
:namespace
语言名(java
) 路径(com.test.thrit.demo
)
【8】文件包含:Thrift
也支持文件包含,相当于C/C++ 中的 include,Java 中的 import。
include "test.thrift"
【9】注释:Thrift
注释支持 shell
风格的注释,支持 C/C++
风格的注释,既#和//开头的都可以当注释,/**/包裹的可以当做注释。
【10】可选和必选:Thrift
提供了两个关键字:required
和 optional
分别用于字段的必填和可选。
# 六、Thrift 安装
官网下载地址:链接 (opens new window)
Thrift compiler for Windows [thrift-0.12.0.exe][PGP][MD5]
将下载的*.exe
所处的目录,配置在path
环境变量中,并重命名为 thrift.exe
即可(测试:thrift -version
)
E:\learnWorkspacesDesign\netty_learn\src\main\java> thrift -version
Thrift version 0.12.0
2
# 七、代码生成
【1】定义一个 .thrift
文件:data.thrift
namespace java com.thrift
#数据类型转化
typedef i16 short
typedef i32 int
typedef i64 log
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 PersionService {
Person getPersonByUsername(1:required String username) throws (1:DataException dataException),
void savePerson(1:required Person person) throws (1:DataException dataException)
}
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
【2】执行命令格式: thrift --gen java
文件路径
E:\learnWorkspacesDesign\netty_learn\src\main\java>thrift --gen java ../../thrift/data.thrift
【3】生成目录结构如下:
【4】导入依赖的 jar
包:
<dependency>
<groupId>org.apache.thrift</groupId>
<artifactId>libthrift</artifactId>
<version>0.12.0</version>
</dependency>
2
3
4
5
# 八、数据传输方式
【1】TSocket
:阻塞式 socket
(效率底);
【2】(常用)TFramedTransport
:以 Frame
为单位进行传输,非阻塞式服务中使用;
【3】TFileTransport
:以文件形式进行传输;
【4】TMemoryTransport
:将内存用于I/O
,Java
实现时内容实际使用了简单的 ByteArraryOutPutStream
;
【5】TZlibTransport
:使用 Zlib
进行压缩与其他传输方式联合使用,当前无 Java
实现;
# 九、 Thrift
支持的服务模式
【1】TSimpleServer
:简单的单线程服务模型,常用语测试;
【2】TThreadPoolServer
:多线程服务模型,使用标准的阻塞式IO
;
【3】TNonblockingServer
:多线程服务模型,使用非阻塞式IO
(需使用 TFramedTransport
传输数据);
【4】THsHaServer
:(常用)THsHa
引入了线程池去处理,其模型把读写任务放到线程池去处理;Half-sync/Half-async
的处理模式,Half-aysnc
是在处理IO
事件上(accept
/read
/write
io
),Half-sync
用于 handler
对 rpc
的同步处理。
# 十、Thrift 服务端与客户端交互
【1】服务端实现类
public class PersionServiceImpl implements PersionService.Iface {
@Override
public Person getPersonByUsername(String username) throws DataException, TException {
Person person = new Person();
person.setAge(2);
person.setUsername("zhengzhaoxiang");
person.setMarried(false);
return person;
}
@Override
public void savePerson(Person person) throws DataException, TException {
System.out.printf("服务端");
System.out.printf(person.getUsername());
}
}
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
【2】thrift
服务端程序编写
public class Server {
public static void main(String[] args) throws Exception {
TNonblockingServerSocket socket = new TNonblockingServerSocket(8899);
//服务 引入了线程池去处理
THsHaServer.Args arg = new THsHaServer.Args(socket).minWorkerThreads(2).maxWorkerThreads(4);
PersionService.Processor<PersionServiceImpl> processor = new PersionService.Processor<>(new PersionServiceImpl());
arg.protocolFactory(new TCompactProtocol.Factory());
arg.transportFactory(new TFramedTransport.Factory());
arg.processorFactory(new TProcessorFactory(processor));
TServer server = new THsHaServer(arg) ;
System.out.println("服务端启动。。。。");
server.serve();
}
}
2
3
4
5
6
7
8
9
10
11
12
13
14
15
【3】thrift
客户端程序编写
public class PersionClient {
public static void main(String[] args) {
TTransport tTransport = new TFramedTransport(new TSocket("localhost",8899),600);
TProtocol protocol = new TCompactProtocol(tTransport);
PersionService.Client client = new PersionService.Client(protocol);
try {
tTransport.open();
Person person = client.getPersonByUsername("zhangsan");
System.out.println(person.getUsername());
System.out.println(person.getAge());
System.out.println(person.isMarried());
System.out.println("---------");
Person person1 = new Person();
person1.setMarried(true);
person1.setAge(20);
person1.setUsername("李四");
client.savePerson(person1);
} catch (Exception e) {
e.printStackTrace();
tTransport.close();
}
}
}
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
【4】测试结果(客户端/服务端)展示:
# 客户端结果展示
zhangsan
2
false
------------
# 服务端结果展示
服务端启动。。。。服务端
李四
2
3
4
5
6
7
8