Thrift简明教程

现如今对于任何一个大型的服务,都不太可能是一个单体的服务。而是由诸多的子服务构成,具体的业务逻辑通过子服务之间的相互调用来完成。这种相互的调用称之为远程调用,也就是通常所说的 RPC。

什么是 RPC

RPC(Remote Procedure Call)是指远程调用,这个和本地调用有很大的区别。下面在 main 方法中调用 helloWorld 就是本地调用。

1
2
3
4
5
6
7
8
public class Main {
public static void final main(String[] args) {
helloWorld(); // 本地调用
}
public static void helloWorld() {
System.out.println("Hello World");
}
}

现在假设 helloWorld 这个方法不在本地,没法直接调用,那么就需要通过远程调用来访问这个方法,在进行远程调用的过程中,就产生了客户端-服务端(Server-Client)模式,服务的提供方就是服务端,服务的调用方就是客户端,客户端和服务端并不是绝对不变的,要看具体的服务的调用流程来认定服务端和客户端。

远程调用的方式有很多,比如 Java 中的 RMI(Remote Method Invocation) 就是典型的 RPC, 还有 Http 也可以认为是一种 RPC。

Thrift 是 FaceBook 实现的一种支持多语言的 RPC 框架,对于主流的编程语言 Java ,C++, Python 等都有很好的支持。 Thrift 会通过自身的 IDL(Interface Description Language) 语言来对接口进行定义,然后 Thrift 通过定义好的 IDL 文件生成相应的接口脚手架,然后只需要分别实现接口脚手架的 Server 端和 Client 端。并将 Server 端进行部署,Client 就可以访问 Server端的服务。

Thrift 特性简介

下图是 Thrift 官方给出的架构图。Thrift 支持的语言多达 28 种,对于各类操作系统也都提供了支持,Thrift 将自身划为4层(不要和网络的四层协议混淆),而且对于各个层已经做好干净的分层和实现。对于不同的业务场景就可以选择不同的协议进行组合。

Thrift 整体上可以分成以下四层:

  • 传输层提供了面向网络 IO 的抽象,在传输层可以使用多种传输协议,TCP、Http 提供了支持
  • 协议层中定义了内存中数据到传输格式的映射机制
  • 处理层封装了从输入流读取数据和从输出流写数据的能力
  • 服务器层将上述的组件合并到一起并按照下列的流程工作:
    • 创建一个 Transport 对象
    • 在 Transport 基础上创建输入\输出 Protocol 对象
    • 在输入\输出 Protocol 基础上创建 Processor 对象
    • 监听到来的连接并移交给 Processor 处理
Thrift 的性能

Thrift 的性能在现有的 RPC 框架中属于前列,这里有详细的 Benchmark,如果特别关注性能,那么 Thrift 是一个很好的选择。

Thrift 的网络模型

而且 Thrift 提供了多种网络模型,支持阻塞服务模型非阻塞服务模型,所有的网络处理模型都继承自 TServer

阻塞服务模型

  • TSimpleServer
    • 最简单的阻塞 IO 模型
    • 一次只能接收和处理一个请求
    • 实际开发基本不会用到
  • TThreadPoolServer
    • 采用阻塞 Socket 的方式工作
    • 主线程负责监听是否有新的 Socket 到达
    • 具体的业务处理交给线程池来处理

非阻塞服务模型

  • TNonblockingServer
    • 单线程模式,但是引入了 NIO 的机制,通过 Channel/Selector 机制来处理事件
  • THsHaServer
    • THsHaServer 继承自 TNonblockingServer
    • 引入了线程池提高了任务的并发处理能力
  • TThreadedSelectorServer
    • 这个模型是对 THsHaServer 模型的一种补充

Thrift的序列化机制

数据在网络的传输过程中,序列化和反序列化是一个很重要的过程,对于不同的系统,对序列化有着不同的要求,Thrift 提供了多种序列化的方式来满足不同的要求。传输协议总体上可以分成文本二进制的两种协议。

Thrift 有如下的传输协议:

  • TBinaryProtocol:二进制编码格式进行数据传输
  • TCompactProtocol:高效率、密集的二进制编码格式进行数据传输
  • TJSONProtocol:使用 JSON 文本的数据编码协议进行数据传输
  • TSimpleJSONProtocol:只提供 JSON 只写的协议,适用于通过脚本语言进行解析

Thrift demo 搭建

Thrift 的安装在官方文档已经介绍的很清楚了。

Thrift 使用 IDL 来定义(Client-Server)之间的接口。假如需要定义一个 hello 的接口,那么就可以定义一个 hello.thrift 文件,文件的内容如下:

1
2
3
service HelloWorldService {
string hello(1: string name)
}

上面定义了一个名为 HelloWorldService 的服务,在这个服务器中有一个叫 hello 的接口,这个接口接受一个 string 类型的参数。

servicestring 都是 IDL 的关键字,IDL 中的关键字包括:

  • 基本数据类型
  • 容器类型
  • 结构体
  • 枚举

这些都是 IDL 的基本数据结构,也还有命名空间、异常、常量等关键字。完整的 IDL文档看这里

上面的 IDL 代码定义好了一个 Thrift 接口的规则,Thrift 就可以根据这个规则生成相应的接口文件。通过这些接口文件,就可以方便的实现相应的业务逻辑,相当于 Thrift 通过 IDL 文件搭出了服务脚手架。

在生成的脚手架文件中,有两个 interface 非常重要:

  • Iface: 这个 interface 由服务器来实现,向客户端提供服务的逻辑在这个接口内实现
  • Client: 这个 interface 由客户端来实现,访问服务端的逻辑在这个接口内实现

AsyncIfaceAsyncClient 是这两个 interface 的异步版本。

下面使用 SimpleServer 来实现这个服务:

Iface 实现:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
public class HelloWorldServiceImpl implements HelloWorldService.Iface{
@Override
public String say(String username) throws TException {
return "Hello " + username;
}
}

/**
*创建一个服务器,向外暴露服务
*/
public class HelloWorldServer {
public static void main(String[] args) throws TTransportException {
TServerSocket serverSocket = new TServerSocket(9090);
HelloWorldService.Processor processor = new HelloWorldService.Processor(new HelloWorldServiceImpl());
TSimpleServer.Args tArgs = new TSimpleServer.Args(serverSocket);
tArgs.processor(processor);
TServer server = new TSimpleServer(tArgs);
server.serve();
}
}

Client实现

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
/**
* 客户端,连接服务器,并且发送消息
*/
public class HelloWorldClient {
public static void main(String[] args) throws TTransportException, TException {
TTransport transport = new TSocket("127.0.0.1", 9090, 3000);
TProtocol protocol = new TBinaryProtocol(transport);
HelloWorldService.Client client = new HelloWorldService.Client(protocol);
try {
transport.open();
String result = client.say("Ray");
System.out.println(result);
} finally {
if (null != transport) {
transport.close();
}
}
}
}

一个简单的 Thrift 的服务也就实现了。

(完)

微信公众号

© 2018 ray