Thrift

1、简介

        Thrift是一种接口描述语言(IDL)和二进制通讯协议,它被用来定义和创建跨语言的服务。它被当作一个远程过程调用(RPC)框架来使用,是由Facebook为“大规模跨语言服务开发”而开发的。它通过一个代码生成引擎联合了一个软件栈,来创建不同程度的、无缝的跨平台高效服务,可以使用C#、C++(基于POSIX兼容系统)、Erlang、Go、Java、Node.js、PHP、Pathon、Ruby等。

2、架构

Thrift软件栈分层从下向上分别为:传输层( TransportLayer)、协议层( ProtocolLayer)、处理层( ProcessorLayer)和服务层( ServerLayer)。

  • 传输层( TransportLayer):传输层负责直接从网络中读取和写入数据,它定义了具体的网络传输协议;比如说 TCP/IP传输等。
  • 协议层( ProtocolLayer):协议层定义了数据传输格式,负责网络传输数据的序列化和反序列化;比如说 JSON、 XML、二进制数据等。
  • 处理层( ProcessorLayer):处理层是由具体的 IDL(接口描述语言)生成的,封装了具体的底层网络传输和序列化方式,并委托给用户实现的 Handler进行处理。
  • 服务层( ServerLayer):整合上述组件,提供具体的网络线程/IO服务模型,形成最终的服务。

在最上层是用户自行实现的业务逻辑代码。    

 

Thrift支持众多通讯协议:

 

  • TBinaryProtocol – 一种简单的二进制格式,简单,但没有为空间效率而优化。比文本协议处理起来更快,但更难于调试。
  • TCompactProtocol – 更紧凑的二进制格式,处理起来通常同样高效。
  • TDebugProtocol – 一种人类可读的文本格式,用来协助调试。
  • TDenseProtocol – 与TCompactProtocol类似,将传输数据的元信息剥离。
  • TJSONProtocol – 使用JSON对数据编码。
  • TSimpleJSONProtocol – 一种只写协议,它不能被Thrift解析,因为它使用JSON时丢弃了元数据。适合用脚本语言来解析。

支持的传输协议有:

  • TFileTransport – 该传输协议会写文件。
  • TFramedTransport – 当使用一个非阻塞服务器时,要求使用这个传输协议。它按帧来发送数据,其中每一帧的开头是长度信息。
  • TMemoryTransport – 使用存储器映射输入输出。(Java的实现使用了一个简单的ByteArrayOutputStream。)
  • TSocket – 使用阻塞的套接字I/O来传输。
  • TZlibTransport – 用zlib执行压缩。用于连接另一个传输协议。

Thrift还提供众多的服务器,包括:

  • TNonblockingServer – 一个多线程服务器,它使用非阻塞I/O(Java的实现使用了NIO通道)。TFramedTransport必须跟这个服务器配套使用。
  • TSimpleServer – 一个单线程服务器,它使用标准的阻塞I/O。测试时很有用。
  • TThreadPoolServer – 一个多线程服务器,它使用标准的阻塞I/O

3、数据类型

基本数据类型:

bool:布尔值,true 或 false,对应 Java 的 boolean
byte:8 位有符号整数,对应 Java 的 byte
i16:16 位有符号整数,对应 Java 的 short
i32:32 位有符号整数,对应 Java 的 int
i64:64 位有符号整数,对应 Java 的 long
double:64 位浮点数,对应 Java 的 double
string:未知编码文本或二进制字符串,对应Java 的 String

结构体类型:

struct:定义公共的对象,类似于C 语言中的结构体定义,在Java 中是一个JavaBean。成员是被正整数编号过的,其中的编号使不能重复的,这个是为了在传输过程中编码使用。

字段会有optional和required之分,但是如果不指定则为无类型–可以不填充该值,但是在序列化传输的时候也会序列化进去,optional是不填充则部序列化,required是必须填充也必须序列化。同一文件可以定义多个struct,也可以定义在不同的文件,进行include引入。每个字段可以设置默认值。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
struct Example {
  1:i32 number=10,
  2:i64 bigNumber,
  3:list<double> decimals,
  4:string name="thrifty"
}
struct People {
    1: required string name; //必填
    2: optional i32 age;  //可选
}
联合(union)
当一个结构体中,field之间的关系是互斥的,即只能有一个field被使用被赋值。可以用union来声明这个结构体,而不是一堆堆optional的field,语意上也更明确了。例如:
union JavaObjectArg {
  1: i32 int_arg;
  2: i64 long_arg;
  3: string string_arg;
  4: bool bool_arg;
  5: binary binary_arg;
  6: double double_arg;
}

容器类型:

集合中的元素可以是除了service之外的任何类型,包括exception。

list: 一系列由T类型的数据组成的有序列表,元素可以重复

set: 一系列由T类型的数据组成的无序集合,元素不可重复

map: 一个字典结构,key为K类型,value为V类型

枚举类型:

和Java差不多

异常类型:

可以自定义异常类型,所定义的异常会继承对应语言的异常基类,例如java,就会继承 java.lang.Exception

1
2
3
4
exception InvalidOperation {
 1: i32 what,
 2: string why
}

服务类型:

thrift定义服务相当于Java中创建Interface一样,创建的service经过代码生成命令之后就会生成客户端和服务端的框架代码。定义形式如下:

1
2
3
4
5
service StringCache {
  void set(1:i32 key, 2:string value),
  string get(1:i32 key) throws (1:KeyNotFound knf),
  void delete(1:i32 key)
}

4、与Dubbo对比

 
Dubbo
Thrift
 
Dubbo
Thrift
开发语言 Java 跨语言
分布式(服务治理) ×
多序列化框架支持 ×(thrift格式)
多种注册中心 ×
管理中心 ×
跨编程语言 ×

5、典型应用场景和非应用场景

       对于需求为高性能,分布式的RPC服务,Thrift是一个优秀的解决方案。它支持众多语言和丰富的数据类型,并对于数据字段的增删具有较强的兼容性。所以非常适用于作为公司内部的面向服务构建(SOA)的标准RPC框架。

        不过Thrift的文档相对比较缺乏,目前使用的群众基础相对较少。另外由于其Server是基于自身的Socket服务,所以在跨防火墙访问时,安全是一个顾虑,所以在公司间进行通讯时需要谨慎。 另外Thrift序列化之后的数据是Binary数组,不具有可读性,调试代码时相对困难。最后,由于Thrift的序列化和框架紧耦合,无法支持向持久层直接读写数据,所以不适合做数据持久化序列化协议。

posted on 2018-07-18 17:24  我&菜鸟  阅读(1266)  评论(0)    收藏  举报