Thrift IDL - 数据类型和服务定义
Thrift 采用 IDL(Interface Definition Language)来定义通用的服务接口,并通过生成不同的语言代理实现来达到跨语言、平台的功能。在 Thrift 的 IDL 中可以定义以下一些类型:基本数据类型,结构体,容器,异常和服务等。
一、 基本数据类型
- bool: 布尔值 (true or false), one byte
- byte: 有符号字节
- i16: 16位有符号整型
- i32: 32位有符号整型
- i64: 64位有符号整型
- double: 64位浮点型
- string: 采用 UTF-8 编码的文本或二进制字符串
基本类型中基本都是有符号数,因为有些语言没有无符号数,所以 Thrift 不支持无符号整型。
二、 特殊类型(括号内为对应的 Java 类型)
binary(ByteBuffer):未经过编码的字节流,这是string类型的一种变形,主要是为java使用
三、 容器类型
Thrift 容器与流行编程语言的容器类型相对应,采用 Java 泛型风格。它有3种可用容器类型:
list
: 元素类型为 t1 的有序表,允许元素重复。对应 c++ 的 vector,java 的 ArrayList 或者其他语言的数组set
:元素类型为 t1 的无序表,不允许元素重复map
: 键类型为 t1,值类型为 t2 的键值对,键不允许重复
容器中元素类型可以是除了 service 外的任何合法 Thrift 类型(包括结构体和异常)。为了最大的兼容性,map 的 key 最好是 Thrift 的基本类型,有些语言不支持复杂类型的 key,JSON 协议只支持那些基本类型的 key。
容器都是同构容器,不是异构容器。容器泛型可以嵌套,诸如list<list<map<string,i32>>>
。
四、结构体(struct)
Thrift 中 struct
定义的是一种对象,和面向对象语言的 class
差不多,但是 struct 有以下一些约束:
- struct 不能继承,可以嵌套,但是不能嵌套自己
- 其成员都要有明确类型
- 成员是被正整数编号过的,其中的编号使不能重复的,这个是为了在传输过程中编码使用
- 成员分割符可以是逗号(,)或是分号(;),而且可以混用,但是为了清晰起见,建议在定义中只使用一种,比如 Java/C++ 学习者可以使用分号(;)
- 字段会有
optional
和required
之分,和 protobuf 一样,但是如果不指定则为无类型,即可以不填充该值,但是在序列化传输的时候也会序列化进去,optional 是不填充则不序列化,required 是必须填充也一定会序列化 - 每个字段可以设置默认值
- 同一文件可以定义多个 struct,也可以定义在不同的文件,进行 include 引入
数字标签作用非常大,随着项目开发的不断发展,也许字段会有变化,但是建议不要轻易修改这些数字标签,修改之后如果没有同步客户端和服务器端会让一方解析出问题。
struct UserProfile
{
1: required string name; //改字段必须填写
2: optional i32 type = 0; //默认值
3: i32 time; //默认字段类型为 optional
}
规范的 struct 定义中的每个域均会使用 required 或者 optional 关键字进行标识。如果 required 标识的域没有赋值,Thrift 将给予提示;如果 optional 标识的域没有赋值,该域将不会被序列化传输;如果某个 optional 标识域有缺省值而用户没有重新赋值,则该域的值一直为缺省值;如果某个 optional 标识域有缺省值或者用户已经重新赋值,而不设置它的 __isset
为 true
,也不会被序列化传输。
五、 异常
异常在语法和功能上类似于(equivalent to)结构体,差别是异常使用关键字 ·exception· 而不是 struct
声明。但它在语义上不同于结构体:当定义一个 RPC 服务时,开发者可能需要声明一个远程方法抛出一个异常。
exception CommonExcpt
{
1: i32 errorCode,
2: string message,
3: string other
}
六、 枚举
很多语言都有枚举,意义都一样。比如,当定义一个消息类型时,它只能是预定义的值列表中的一个,可以用枚举实现。
enum TweetType {
TWEET, // (1)
RETWEET = 2, // (2)
DM = 0xa, // (3)
REPLY
} // (4)
struct Tweet {
1: required i32 userId;
2: required string userName;
3: required string text;
4: optional Location loc;
5: optional TweetType tweetType = TweetType.TWEET; // (5)
16: optional string language = "english"
}
说明:
- 编译器默认从0开始赋值
- 可以赋予某个常量某个整数
- 允许常量是十六进制整数
- 末尾没有分号
- 给常量赋缺省值时,使用常量的全称
注意,不同于 protocal buffer,Thrift 不支持枚举类嵌套,枚举常量必须是32位的正整数.
七、 常量定义和类型定义
Thrift 允许定义跨语言使用的常量,复杂的类型和结构体可使用JSON形式表示。
const i32 INT_CONST = 1234; // (1)
说明:分号可有可无。支持16进制。
Thrift 支持 C/C++ 类型定义。
typedef i32 MyInteger // a // b
typedef i64 UserId
说明:a.末尾没有逗号。b. Struct也可以使用typedef。
八、 服务定义
服务的定义方法在语义(semantically)上等同于面向对象语言中的接口。Thrift 编译器会产生执行这些接口的 client 和 server stub。
在流行的序列化/反序列化框架(如 protocal buffer)中,Thrift 是少有的提供多语言间 RPC 服务的框架。这是 Thrift 的一大特色。
Thrift 编译器会根据选择的目标语言为 server 产生服务接口代码,为 client 产生 stubs。
service Twitter {
// A method definition looks like C code. It has a return type, arguments,
// and optionally a list of exceptions that it may throw. Note that argument
// lists and exception list are specified using the exact same syntax as
// field lists in structs.
void ping(), // (1)
bool postTweet(1:Tweet tweet); // (2)
TweetSearchResult searchTweets(1:string query); // (3)
// The 'oneway' modifier indicates that the client only makes a request and
// does not wait for any response at all. Oneway methods MUST be void.
oneway void zip() // (4)
}
几个注意点:
- 接口支持以逗号和分号结束
- 参数可以是基本类型和结构体;(参数是 cosnt 的,转换为 c++ 语言是 const&)
- 返回值是 void的,注意一定要用
oneway
修饰 - 服务支持继承
- 服务不支持重载
九、 命名空间
Thrift 中的命名空间类似于 C++ 中的 namespace 和 java 中的 package,它们提供了一种组织(隔离)代码的简便方式。名字空间也可以用于解决类型定义中的名字冲突。
由于每种语言均有自己的命名空间定义方式(如 python 中有 module), Thrift 允许开发者针对特定语言定义 namespace:
namespace cpp com.example.project // (1)
namespace java com.example.project // (2)
namespace php com.example.project
十、 包含引用
为了便于管理、重用和提高模块性/组织性,我们常常分割 Thrift 定义在不同的文件中。包含文件搜索方式与 c++ 一样。Thrift 允许文件包含其它 Thrift 文件,用户需要使用 Thrift 文件名作为前缀访问被包含的对象,如:
include "tweet.thrift" // (1)
struct TweetSearchResult {
1: tweet.Tweet tweet; // (2)
}
说明:
- Thrift 文件名要用双引号包含,末尾没有逗号或者分号
- 注意文件名作为引用前缀,如
tweet.Tweet
十一、 注释
Thrfit 支持 shell 注释风格,Java/C/C++ 语言中单行或者多行注释风格。
# This is a valid comment.
/*
* This is a multi-line comment.
* Just like in C.
*/
// C++/Java style single-line comments work just as well.
Thrift 实例请参考《精选实例:Thrift 实例教程(Java) 》,多接口服务支持请参考《Thrift 多接口服务支持:server 绑定多个 processor 》,学习Thrift 服务模型和序列化机制请参考《Thrift 服务模型和序列化机制深入学习》。