Thrift 是一个轻量级、跨语言的 RPC 框架,由 facebook 开发,2007年正式开源,2008 纳入 Apache 软件基金会开源项目。
Thrift 支持多种编程语言,包括 C++, Java, Python, PHP, Ruby, Erlang, Perl, Go, C#, JavaScript, Node.js 等。
安装
安装 thrift 编译器,它用来生成所需语言的代码文件,例如生成 go 语言代码:
thrift -r --gen go tutorial.thrift 复制代码
- mac
建议直接使用brew安装:
brew install thrift 复制代码
也可以下载源码安装,参考:http://thrift.apache.org/docs/install/os_x
- CentOS
需下载源码安装,参考:http://thrift.apache.org/docs/install/centos
- Debian/Ubuntu
- windows
本文以 windows 环境为例,从官方下载 exe 文件,下载地址:http://archive.apache.org/dist/thrift/%EF%BC%8C%E4%B8%8B%E8%BD%BD%E6%9C%80%E6%96%B0%E7%89%88%EF%BC%8C%E5%A6%82%E4%B8%8B%E5%9B%BE
将下载的 exe 文件改名为 thrift.exe,然后可以放到任意PATH路径下面,例如放在$GOROOT/bin下:
打开 cmd,验证是否成功:
看到如上版本号,就说明thrift安装成功了。
Thrift 类型
Thrift 中的类型包括基础类型、结构、容器、异常、服务等几个部分。(官网介绍:https://thrift.apache.org/docs/types
)
- 基本类型
bool
:布尔型,1个字节byte
:有符号整数,1个字节i16
:有符号16位整型i32
:有符号32位整型i64
:有符号64位整型double
:64位浮点数值string
:字符串类型,使用UTF-8编码
- 特殊类型
binary
:二进制数据类型(字节数组)
- 结构
Thrift 结构定义了一个公共对象,它们本质上等同于OOP语言中的类,但没有继承。结构有一组强类型字段,每个字段都具有唯一的名称标识符。字段可能有各种注释(数字字段ID、可选默认值等)。thrift IDL(Interface Definition Language)规范详见官网介绍:thrift.apache.org/docs/idl
示例:
struct SharedStruct { 1: i32 key = 0, 2: string value, } 复制代码
- 结构体中每一个域都有一个正整数标识符,这个标识符并不要求连续,但一旦定义,不建议再进行修改。
- 每个域前都会有
required
或optional
:
required
:必填域,如果设置了 required,在实际构造结构体时就必须给这个域赋值,否则 thrift 会认为这是一个异常。optional
:可选域,如果设置了 optional,在实际构造结构体时没有给这个域赋值,则在使用这个结构体时,就会忽略掉这个 optional 域。- 域是可以设置默认值的,如上
key = 0
。
- 容器
有三种容器类型:
list<t1>
:单类型有序列表,允许有重复元素,会被转换成C++中的vector,Java中的 ArrayLis等set<t1>
:单类型无需集合,不允许有重复元素,会转换成C++中的set,Java中的HashSet、Python中的Set等map<t1,t2>
:Map型(key:value),会被转换成C++中的map,Java中的HashMap,PHP中的关联数组等
容器元素可以是任何有效的Thrift 类型,注意:map 的key类型需要是基础类型,很多开发语言并不支持 map 的 key 类型为复杂数据类型。
- 异常
异常在语法上和结构的用法是完全一致的,不过 exception 是在远程调用发生异常时用来抛出异常用的。
- 服务
服务是由 Thrift 类型定义的。服务的定义在语义上等同于面向对象中定义接口(或纯虚拟抽象类)。Thrift 编译工具会根据定义的服务来生成对应的方法和函数。
thrift 文件解析
注释
thrift 文件注释有三种形式:
# /**/ // 复制代码
示例解析
/** * .thrift文件可以引用其他.thrift文件,这样可以方便地把一些公共结构和服务引入进来。 * 在引用其他.thrift文件时,既可以直接引用当前文件夹下的文件,也可以引用其他路径下的文件,但后者需要在thrift编译工具编译时加上 -I 选项来设定路径。 * 如果希望访问被包含的.thrift文件中的内容,则需要使用.thrift文件的文件名作为前缀,比如 shared.SharedObject。 * 本例中引用了文件 shared.thrift */ include "shared.thrift" /** * thrift支持针对不同的语言设置不同的 namespace,thrift会在生成不同语言代码时,进行相应的设置。 */ namespace java tutorial namespace go tutorial // 使用 typedef 定义别名 typedef i32 MyInteger /** * 定义常量. 复杂的类型和结构体使用JSON表示法. */ const i32 INT32CONSTANT = 9853 const map<string,string> MAPCONSTANT = {'hello':'world', 'goodnight':'moon'} /** * 枚举是32位整型数字, 如果没有显式指定值,默认从1开始编号并递增. */ enum Operation { ADD = 1, SUBTRACT = 2, MULTIPLY = 3, DIVIDE = 4 } /** * 结构体由一组成员来组成, 一个成员包括数字标识符, 类型, 符号名, 及一个可选的默认值. * 每个域都可以设置为optional或required来表示是否为必填域, * 以便thrift决定是否在数据传输时要包含这个域。 * 不指定时,默认为required。 */ struct Work { 1: i32 num1 = 0, 2: i32 num2, 3: Operation op, 4: optional string comment, } // 结构体也可以作为异常,在语法上,异常的定义方式和结构体是完全一样的 exception InvalidOperation { 1: i32 whatOp, 2: string why } /** * 定义服务需要一个服务名, 加上一个可选的继承, 使用extends关键字 */ service Calculator extends shared.SharedService { /** * 服务中方法的定义非常类似于C语言的语法。包括一个返回值, * 一个参数列表以及一个可以抛出的异常列表(可选) * 定义参数列表的方法、定义异常列表的方法,和定义结构体的方法 * 都是相似的,可以从下面的例子中看出。 * 注意,除了最后一个方法,其他的方法最后都要有一个逗号。 */ void ping(), i32 add(1:i32 num1, 2:i32 num2), /** * 在异常列表前,需要加throws关键字。 */ i32 calculate(1:i32 logid, 2:Work w) throws (1:InvalidOperation ouch), /** * 这个方法有 oneway 修饰符, 表示客户段发送一个请求后会立即返回, * 不会等待回应(oneway在英语里就是“单向”的意思)。 * oneway 方法的返回值必须是 void */ oneway void zip() }