对Thrift的一点点理解
这是一篇学习Thrift的笔记,包含了这样几点内容:
- 简单介绍Thrift
- 怎样使用Thrift
- Thrift整体架构
- Thrift中的知识点
struct可以设置默认值
thrift中的序列化机制
thrift中的版本控制
简单介绍Thrift
它是一款RPC通信框架,采用C/S架构,且拥有高效的序列化机制。要使用Thrift,首先我们需要在远端服务器上开启Thrift服务,之后,服务器端进程保持睡眠状态,直到客户端代码的调用。
Thrift应用广泛的一个主要原因是它支持多种主流的语言,且使用它的用户不需要关注服务器和客户端是怎样实现通信,怎样实现序列化的,只需要去考虑怎样实现自己需要的业务逻辑。
Thrift使用接口语言定义数据结构和服务,包含了最常用的数据类型,并一一对应各种语言的基本类型,还可以定义枚举和异常等等。
怎样使用Thrift
Thrift把它定义的相当简洁,以致于我们的使用过程也是异常的方便,简单来说,使用Thrift的过程只是需要以下的四个步骤:
1. 设计需要交互的数据格式(struct、enum等等)和具体的服务(service),定义thrift接口描述文件,也就是后缀名是 .thrift
2. 利用thrift工具(我使用的是比较老的版本0.5.0),根据之前定义的接口文件生成目标语言文件(在这次的笔记中客户端代码和服务器代码都是使用java语言)
3. 实现服务(service)代码,并把实现的业务逻辑设定为thrift服务器的处理层,选择端口,服务器启动监听,等待客户端的连接请求
4. 客户端使用相同的端口连接服务器请求服务
下面简单的介绍下thrift接口描述语言(IDL)的类型:
IDL包含基础类型、结构、容器、异常和服务这样几种类型:
基础类型 : 包括了 bool,byte、i16,i32,i64,double,string,每一种都对应各种语言的基础类型
结构 : 在thrift中定义为struct,它类似于C语言中的结构体,是基础类型的集合体,每一个结构都会生成一个单独的类,在java中类似于pojo
容器 : thrift中定义了常用的三种容器 – list,set,map,在Java中各自的对应实现是 ArrayList、HashSet、HashMap,其中模板类型可以是基础类型或者结构类型
异常 : 异常的定义类似于struct,只是换成了exception
服务 : 服务类似于java中的接口,需要对服务中的每一个方法签名定义返回类型、参数声明、抛出的异常,对于方法抛出的异常,除了自己声明的之外,每个方法还都会抛出TException,对于返回值是void类型的方法,我们可以在方法签名的前面加上oneway标识符,将这个方法标记为异步的模式,即调用之后会立即返回
下面,为了更好的理解怎样使用thrift,以及怎样使用IDL中的类型,我将举一个例子,当然,这个例子只是为了演示过程,并没有过多的设计,可能会存在一些并不实用的逻辑。
怎样开始写这个例子呢?对呀,就按照之前介绍的Thrift过程的四个步骤就可以了:
- 定义接口描述文件(.thrift)
qinyi_student_model.thrift 定义学生信息和学校信息的数据结构
/**
* qinyi student thrift model
* @author qinyi
* @since 2015-10-02
*/
namespace java com.qinyi.thrift_study.thrift_example
enum Sex {
Boy = 1;
Girl = 2;
}
struct StudentInfo {
1: required string name;
2: required Sex sex;
3: required i32 age;
4: optional list<string> hobby;
5: required map<string, i64> number;
}
struct School {
1: required string name;
2: required list<StudentInfo> students;
3: optional string description;
}
可以看到,我们使用namespace定义文件的命名空间,由于目标代码是java语言,所以namespace java之后的声明代表的就是包名,struct结构中每一个属性前都有一个数字id标识,这个一旦定义了,最好不要去更改,具体的原因下文会有具体说明,属性类型前有required/optional声明,代表这个属性是必须要设置的或者可以选择不设置,如果这个属性被声明为required,但是在代码中没有set,thrift会认为这是一个异常,当然,我们可以对属性设置默认值,就是声明的时候赋值就可以了。文件开始的部分使用的java风格的注释,这也是可选的,thrift支持c,c++,shell,java风格的注释,怎样注释根据个人习惯就好。
qinyi_student_exception.thrift 定义异常
/**
* qinyi student thrift exception
* @author qinyi
* @since 2015-10-02
*/
namespace java com.qinyi.thrift_study.thrift_example
exception StudentException {
1: required i64 errorCode;
2: required string description;
3: optional string causeInfo;
}
我们可以看到,异常的定义和上面文件中的struct是极为相似的。
qinyi_student_service.thrift 定义服务
/**
* qinyi student thrift service
* @author qinyi
* @since 2015-10-02
*/
namespace java com.qinyi.thrift_study.thrift_example
include "qinyi_student_model.thrift"
include "qinyi_student_exception.thrift"
// 一个服务的定义在语义上相当于面向对象编程中的一个接口
service StudentService {
// add student to school
bool addStudentToSchool(1: qinyi_student_model.StudentInfo student) throws (1: qinyi_student_exception.StudentException ex);
// get student info by name
list<qinyi_student_model.StudentInfo> getStudentInfoByName(1: string name) throws (1: qinyi_student_exception.StudentException ex);
// print single student info
void printStudentInfo(1: qinyi_student_model.StudentInfo student) throws (1: qinyi_student_exception.Stu