Thrift工具的设计介绍
1 前言
随着facebook流量的增加与网络结构的扩展,许多在站点操作(如:搜索,广告选择,交付,事件日志)的资源需求所提出的技术要求远远的超出了LAMP架构的范围.并且Facebook团队在开发应用选择编程语言时是基于选择最合适的语言干最适合的事情理念的,这就产生了跨语言访问的问题,这样thrift在facebook被提了出来,最后贡献给Apache开源了
Thrift是一个软件库与代码生成工具集合,Facebook为了开发与实现高效且可扩展的后端服务而发明了它。为了使跨语言编程的远程通信变的高效与可靠,Thrift抽取每种语言中主要通信的组件形成公共库,公共库包括多种语言的实现。一般地,Thrift允许开发者使用一种中间语言自定义数据类型与服务接口,进而生成构建RPC客户端与服务器端代码
这样Thrift的一个最终实现就是通过一个中间语言(IDL, 接口定义语言)来定义服务接口和数据类型,然后通过一个Thrift的代码生成引擎生成不同语言的代码(目前支持C++,Java, Python, PHP, Ruby, Erlang, Perl, Haskell, C#, Cocoa,Smalltalk和OCaml)
为了满足跨语言的调用及通信的环境,Thrift主要定义了一下几个重要组件
Types:Thrift有个数据类型系统,这个系统根据Thrift规定的几种基本类型,能产生多种语言中的对应类型,而不需要开发者写Thrift数据类型到目标语言的序列化代码
Transport:每一种语言都需要提供一组相互传递消息的接口,而具体怎么传输不应该是开发者所实现的。Transport组件解耦了数据传输层与开发代码层
Protocol:数据传输的格式也不应该是开发者所关注的,Protocol组件提供一组数据解码与编码的方法。
Processors.:Processors 是一组包装器,主要包装了Protocol,用于处理调用远程方法的数据流(读写)
2 框架
Thrift提供了一种中间语言IDL (Interface Definition Language),允许开发者定义自己的数据结构,并且可加入最小的额外信息告诉Thrift代码生成器产生安全的各种语言对象。使用IDL定义结构体,是开发者不必关心目标语言与IDL之间的类型映射,开发者只需把保存好的.thrift交付Thrift指令即可生成各种语言代码。Thrift网络结构如下图
2.1 Types
数据类型系统是基于几个基本数据类型的。在考虑到哪种类型需要支持时,Thrift致力于清晰明了的类型而不是丰富的类型,同时关注能够适用于所有编程语言的关键类型,放弃那些仅仅适用于个别语言的类型.thrift的支持的数据类型有BaseType,Struct,Exception,Service
Base Types
Thrift支持以下基本数据类型
bool:布尔类型(true orvalue),占一个字节
byte:有符号字节
i16:16位有符号整型
i32:32位有符号整型
i64:64位有符号整型
double:64位浮点数
string:未知编码或者二进制的字符串
注意:thrift不支持无符号整型,原因很简单,因为很多目标语言支持在无符号整型(如java)。至于为何很多语言不支持无符号整形,据说是因为无符号类型会使很多语言的优势丧失,即支持代价太大.
Structs
Stucts是聚合一组Thrift数据类型的对象,语法与C语言的结构体类似,struct中的元素类型都是强数据类型,每一个元素的标示符唯一。Struct被转换为面向对象语言的中的类
Exception
Exception基本与Struct相同,不同的是使用Exception关键字定义。在每一种目标语言中,生成的对象继承自一个异常基类都是合适的(因为在面向对象的远程方法中可能需要抛出异常),因此Exception类型可以无缝隙的组合目标语言中的异常处理,即重点强调产生开发者熟悉的代码.
Structs例子
struct Example {
1:i32 number=10,
2:i64 bigNumber,
3:double decimals,
4:string name="thrifty"
}
Services
Services也被定义为Thrift Type的一种,Services的定义在语法上等同于面向对象语言中定义接口。Thrift编译器会产生实现这些接口的client和server Stub。Service的定义语法下:
service <name> {
<returntype> <name>(<arguments>)
[throws (<exceptions>)]
...
}
如:
service StringCache {
void set(1:i32 key, 2:string value),
string get(1:i32 key) throws (1:Key NotFound knf),
void delete(1:i32 key),
async void save(1:i32 key)
}
注意,Service中除了使用Thrift的类型为,另外void也可以当做一种类型使用,一把用作返回值类型 ,另外在void 加加入async关键字表示为异步方法。函数中参数列表的定义方式与struct完全相同.
2.2 Transport
Transport层提供了一个简单的网络读写抽象层。Thrift设计重点之一是解耦传输层与用户自定义的代码层。彻底地讲,Thrift产生的目标语言代码只需知道怎么读写数据即可,至于数据的来源与去向都是无关紧要的,比如:数据来源与去向可以是网络,内存,磁盘文件等。
以下是一些Transport接口提供的方法:
open
close
read
write
flush
除了以上几个接口,Thrift使用ServerTransport接口接受或者创建原始transport对象。正如名字暗示的那样,ServerTransport用在server端,为到来的连接创建Transport对象。
open
listen
accept
close
2.3 Protocol
Protocol抽象层定义了一种将内存中数据结构映射成可传输格式的机制,他的目的简单而明确,只做两件事情:解码与编码数据
Protocol接口的定义如下:
writeMessageBegin(name, type,seq)
writeMessageEnd()
writeStructBegin(name)
writeStructEnd()
writeFieldBegin(name, type, id)
writeFieldEnd()
writeFieldStop()
writeMapBegin(ktype, vtype,size)
writeMapEnd()
writeListBegin(etype, size)
writeListEnd()
writeSetBegin(etype, size)
writeSetEnd()
writeBool(bool)
writeByte(byte)
writeI16(i16)
writeI32(i32)
writeI64(i64)
writeDouble(double)
writeString(string)
name, type, seq =readMessageBegin()
readMessageEnd()
name = readStructBegin()
readStructEnd()
name, type, id =readFieldBegin()
readFieldEnd()
k, v, size = readMapBegin()
readMapEnd()
etype, size = readListBegin()
readListEnd()
etype, size = readSetBegin()
readSetEnd()
bool = readBool()
byte = readByte()
i16 = readI16()
i32 = readI32()
i64 = readI64()
double = readDouble()
string = readString()
下面是一些对大部分thrift支持的语言均可用的protocol:
binary:简单的二进制编码
Compact
Json
2.4 Processor
Processor封装了从输入数据流中读数据和向数据数据流中写数据的操作。读写数据流用Protocol对象表示。
Processor的结构体非常简单:
interface TProcessor {
bool process(TProtocol in, TProtocol out) throws TException
}
与服务相关的processor实现由编译器产生。Processor主要工作流程如下:从连接中读取数据(使用输入protocol),将处理授权给handler(由用户实现),最后将结果写到连接上(使用输出protocol)。
2.5 Server
Server将以上所有特性集成在一起:
创建一个transport对象
为transport对象创建输入输出protocol
基于输入输出protocol创建processor
等待连接请求并将之交给processor处理