Thrift 是一个基于静态代码生成的跨语言的RPC协议栈实现,它可以生成包括 C++, Java, Python, Ruby, PHP 等主流语言的代码,这些代码实现了 RPC 的协议层和传输层功能,从而让用户可以集中精力于服务的调用和实现。
1. 软件栈
Thrift 对软件栈的定义非常的清晰, 使得各个组件能够松散的耦合, 针对不同的应用场景, 选择不同的方式去搭建服务。
- 传输层(Transport Layer):传输层负责直接从网络中读取和写入数据,它定义了具体的网络传输协议;比如说 TCP/IP 传输、MemoryBuffer 等
- 协议层(Protocol Layer):协议层定义了数据传输格式,负责网络传输数据的序列化和反序列化;比如说 JSON、XML、二进制数据等
- 处理层(Processor Layer):处理层作为协议层和用户提供的服务实现之间的纽带,负责调用服务实现的接口,是由具体的IDL(接口描述语言)生成的,封装了具体的底层网络传输和序列化方式,并委托给用户实现的 Handler 进行处理
- 服务层(Server Layer):整合上述组件,根据 Thrift 定义的服务接口描述文件生成的客户端和服务器端代码框架,提供具体的网络线程/IO 服务模型,形成最终的服务
- 业务逻辑层(Your code)
接下来我们按照自底向上的顺序介绍 thrift 的各个模块。
1.1. 传输层(Transport Layer)
Transport 与网络数据通信相关,thrift 通信协议有基于 TCP/IP 协议的实现。在现在的网络通信服务器中, TCP/IP 协议栈由 socket 来实现。Thrift 也不例外,在 thrift 源码中,是通过将 socket 包装成各种功能不同的 TTransport
来使用:
TSocket
:使用阻塞式 I/O 进行传输,是常见的模式TNonblockingTransport
:使用非阻塞方式,用于构建异步客户端TFrameTransport
:使用非阻塞方式,按块的大小进行传输
1.2. 协议层(Protocol Layer)
Protocol 是 transport 的上一层,transport 负责数据传输,protocol 负责对数据进行解析,将数据解析成对应的数据结构代码,供程序直接调用。Thrift 支持各种语言,通过一个 x.thrift
的接口描述文件来通信。Thrift 的接口描述文件是各种语言通用的,其通过 thrift compiler 来生成对应的源代码,例如 C++ 对应的命令为 thrift --gen cpp x.thrift
。
Thrift 可以让用户选择客户端与服务端之间传输通信协议的类别,在传输协议上总体划分为文本(text)和二进制(binary)传输协议。为节约带宽,提高传输效率,一般情况下使用二进制类型的传输协议为多数,有时还会使用基于文本类型的协议,这需要根据项目/产品中的实际需求。常用协议有以下几种:
TBinaryProtocol
:二进制编码格式进行数据传输TCompactProtocol
:高效率的、密集的二进制编码格式进行数据传输TJSONProtocol
:使用 JSON 文本的数据编码协议进行数据传输TSimpleJSONProtocol
:只提供 JSON 只写的协议,适用于通过脚本语言解析
1.3. 处理层(Processor Layer)
服务调用组件。 Processor 封装了从输入流读取数据和向输出流写入数据的能力。输入和输出流由 Protocol objectss 表示。
|
|
1.4. 服务层(Server Layer)
Server 主要做以下工作:
- Create a transport
- Create input/output protocols for the transport
- Create a processor based on the input/output protocols
- Wait for incoming connections and hand them off to the processor
根据单线程/多线程,同步/异步分为以下几种:
TSimpleServer
:单线程服务器端,使用标准的阻塞式 I/OTThreadPoolServer
:多线程服务器端,使用标准的阻塞式 I/OTNonblockingServer
:单线程服务器端,使用非阻塞式 I/OTHsHaServer
:半同步半异步服务器端,基于非阻塞式 I/O 读写和多线程工作任务处理TThreadedSelectorServer
:多线程选择器服务器端,对THsHaServer
在异步 I/O 模型上进行增强
2. 特点
-
开发速度快
通过编写 RPC 接口 Thrift IDL 文件,利用编译生成器自动生成服务端骨架(Skeletons)和客户端桩(Stubs)。从而省去开发者自定义和维护接口编解码、消息传输、服务器多线程模型等基础工作。
- 服务端:只需要按照服务骨架即接口,编写好具体的业务处理程序(Handler)即实现类即可。
- 客户端:只需要拷贝 IDL 定义好的客户端桩和服务对象,然后就像调用本地对象的方法一样调用远端服务。
-
接口维护简单
通过维护 Thrift 格式的 IDL(接口描述语言)文件(注意写好注释),即可作为给 Client 使用的接口文档使用,也自动生成接口代码,始终保持代码和文档的一致性。且 Thrift 协议可灵活支持接口的可扩展性。
3. 数据类型
Thrifty 的数据类型包括预定义的基本类型,容器类型,用户自定义的结构体和异常,以及服务。
-
基本类型(Base Type)
bool
:布尔值,一个字节i8(byte)
:8 位有符号整数i16
:16 位有符号整数i32
:32 位有符号整数i64
:64 位有符号整数double
:64 位浮点数binary
:一个字节数组string
:编码不可知的文本或二进制字符串
-
容器类型(Container)
list<t>
:元素类型为 t 的有序列表,容许元素重复set<t>
:元素类型为 t 的无序表,不容许元素重复map<k,v>
:key/value 映射,key 不允许重复- 其中容器中的元素类型可以是除了
service
外的任何合法 Thrift 类型(包括结构体和异常)
-
结构体类型
struct
Thrift 中的
struct
类似于 C 语言,其在面向对象的语言中转换为class
。一个struct
其由多个field
组成,每个field
包括唯一的整数标识符、type
、name
和可选的默认值组成。1 2 3 4 5 6 7 8 9 10 11 12 13
struct Location{ 1: required double latitude; 2: required double longitude; } // 没有逗号 struct Tweet{ 1: required i32 userId; 2: required string userName; 3: required string text; 4: optional Location loc; 5: optional TweetType tweetType = TweetType.TWEET; // 详见 enum 16: optional string language = "Endlish"; }
field
可用关键字optional
或required
进行标识struct
不能继承,但是可以嵌套,但不能嵌套自己- 同一文件可以定义多个
struct
,也可以定义在不同的文件中,利用include
导入
-
异常类型
exception
异常在语法和功能上相当于结构体,差别是异常使用关键字
exception
而不是struct
声明。它在语义上不同于结构体:当定义一个 RPC 服务时,开发者可能需要声明一个远程方法抛出一个异常。 -
服务类型
service
服务的定义方法在语义上等同于面向对象语言中的接口,Thrift compiler 将用你所选择的语言据此生成 service interface code (for the server) and stubs (for the client)
1 2 3 4 5 6 7 8 9 10 11 12 13
service Twitter{ // A method definition looks like C code. It has a return type, arguments // and optionally a list of exceptions that it may throw. Note that argument // lists and exception list are specified using the exact same syntax as // field lists in structs. void ping(), bool postTweet(1:Tweet tweet) throws (1:TwitterUnavailable unavailable), TweetSearchResult searchTweets(1:string query); // The 'oneway' modifier indicates that the client only makes a request and // does not wait for any response at all. Oneway methods MUST be void. oneway void zip(); }
- 函数定义可以用分号或者逗号结尾
- 参数和返回值类型可以是基本类型或者结构体
void
是函数的有效返回类型- 服务支持继承,一个
service
可使用extends
关键字继承另一个service
- 服务不支持重载
4. 其他特性
4.1. Typedef
Thrift 支持 C/C++ 风格的 typedef
|
|
4.2. enum
Thrift 支持 C 风格的 enum ,编译器从 0 开始分配默认值。Thrift 还不支持嵌套枚举,枚举常量大小不超过 32 位正整数。
|
|
4.3. constant
Thrift 允许定义跨语言使用的常量,复杂类型和结构体使用 JSON 格式赋值。
|
|
5. Generated Files(C++)
- All constants go into a single
.cpp/.h
pair - All type definitions (enums and structs) go into another
.cpp/.h
pair - Each service gets its own
.cpp/.h
pair
|
|
Thrift 数据类型和 C++ 数据类型对应表
Thrift | C++ |
---|---|
bool | bool |
byte | int8_t |
i16 | int16_t |
i32 | int32_t |
i64 | int64_t |
double | double |
binary | std::string |
string | std::string |
list |
std::vector |
set |
std::set |
map<t1, t2> | std::map<t1, t2> |