小知识,大挑战!本文正在参与“程序员必备小知识”创作活动。
基本介绍
- 跨语言的服务部署框架;
- 最初由Facebook于2007年开发,2008年进入Apache开源项目;
- 通过IDL(Interface Definition Language,接口定义语言)来定义RPC(Remote Procedure Call,远程过程调用)的接口和数据类型;
- 使用Thrift编译器生成不同语言的代码(目前支持C++,Java, Python, PHP, Ruby, Erlang, Perl, Haskell, C#, Cocoa, Smalltalk和OCaml)。
架构图
TTransport
定义了数据传输方式,可以为TCP/IP传输,内存共享或者文件共享等被用作运行时库。
- TSocket:阻塞式socker;
- TFramedTransport:以frame为单位进行传输,非阻塞式服务中使用;
- TFileTransport:以文件形式进行传输;
- TMemoryTransport:将内存用于I/O,java实现时内部实际使用了简单的ByteArrayOutputStream;
- TZlibTransport:使用zlib进行压缩, 与其他传输方式联合使用,当前无java实现。
常见方法:open,close,read,write,flush,listen,accept。
TProtocol
定义了一种将内存中数据结构映射成可传输格式的机制。换句话说,Protocol定义了数据类型怎样使用底层的Transport对自己进行编解码。因此,Protocol的实现要给出编码机制并负责对数据进行序列化。
数据传输格式:
- TBinaryProtocol:二进制格式;
- TCompactProtocol:压缩格式;
- TJSONProtocol:JSON格式;
- TSimpleJSONProtocol:提供JSON只写协议, 生成的文件很容易通过脚本语言解析;
- TDebugProtocol:使用易懂的可读的文本格式,以便于debug
TProcessor
封装了从输入数据流中读数据和向数据输出流中写数据的操作。读写数据流用Protocol对象表示。
与服务相关的processor实现由编译器产生。Processor主要工作流程如下:从连接中读取数据(使用输入protocol),将处理授权给handler(由用户实现),最后将结果写到连接上(使用输出protocol)。
TServer
将以上所有特性集成在一起:
(1) 创建一个Transport对象
(2) 为Transport对象创建输入输出Protocol
(3) 基于输入输出Protocol创建Processor
(4) 等待连接请求并将之交给Processor处理
Thrift支持的服务模型
- TSimpleServer:简单的单线程服务模型,常用于测试;
- TThreadPoolServer:多线程服务模型,使用标准的阻塞式IO;
- TNonblockingServer:多线程服务模型,使用非阻塞式IO(需使用TFramedTransport数据传输方式);
IDL文件(.thrift)
命名空间
namespace cpp com.example.project
namespace java com.example.project
基本数据类型
- bool,布尔型(true or false);
- byte,有符号8位整型;
- i16,有符号16位整型;
- i32,有符号32位整型;
- i64,有符号64位整型;
- double,64位浮点数;
- string,字符串(字符集:UTF-8);
- binary,字节数组;
注意:thrift不支持无符号整型,是因为在很多语言里面不支持无符号整型。
容器
- map<t1,t2>,字典;
- list,列表;
- set,集合;
枚举
enum TweetType {
TWEET, // 编译器默认从1开始赋值
RETWEET = 2, // 可以赋予某个常量某个整数
DM = 0xa, //允许常量是十六进制整数
REPLY // 末尾没有逗号
}
常量
const i32 INT_CONST = 1234; // 分号是可选的
const map<string,string> MAP_CONST = {"hello": "world", "goodnight": "moon"}
结构体
结构体中,每个字段包含一个整数ID,数据类型、字段名,和一个可选的默认值。字段还可以声明为"optional",当该字段没有设置的时候,不会被序列化输出;
规范的struct定义中的每个域均会使用required或者optional关键字进行标识。如果required标识的域没有赋值,thrift将给予提示。如果optional标识的域没有赋值,该域将不会被序列化传输。如果某个optional标识域有缺省值而用户没有重新赋值,则该域的值一直为缺省值。
struct Tweet {
1: required i32 userId;
2: required string userName;
3: required string text;
4: optional TweetType tweetType = TweetType.TWEET // 给常量赋缺省值时,使用常量的全称
5: optional string language = "english"
}
异常
异常在语法和功能上类似于结构体,只不过异常使用关键字exception而不是struct关键字声明。但它在语义上不同于结构体,当定义一个RPC服务时,开发者可能需要声明一个远程方法抛出一个异常。
exception InvalidOperation {
1: i32 what,
2: string why
}
服务
在流行的序列化/反序列化框架(如protocol buffer)中,Thrift是少有的提供多语言间RPC服务的框架。
Thrift编译器会根据选择的目标语言为server产生服务接口代码,为client产生桩代码。
//“Twitter”与“{”之间需要有空格!!!
service Twitter {
// 方法定义方式类似于C语言中的方式,它有一个返回值,一系列参数和可选的异常
// 列. 注意,参数列表和异常列表定义方式与结构体中域定义方式一致.
void ping(), // 函数定义可以使用逗号或者分号标识结束
bool postTweet(1:Tweet tweet); // 参数可以是基本类型或者结构体,参数是只读的(const),不可以作为返回值!!!
TweetSearchResult searchTweets(1:string query); // 返回值可以是基本类型或者结构体
// ”oneway”标识符表示client发出请求后不必等待回复(非阻塞)直接进行下面的操作,
// ”oneway”方法的返回值必须是void
oneway void zip() // 返回值可以是void
}
Service中的函数,其参数列表的定义方式与struct完全一样;
Service支持继承,一个service可使用extends关键字继承另一个service,struct不支持继承;