Thrift IDL - 数据类型和服务定义

原创 thriftidl

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 有以下一些约束:

  1. struct 不能继承,可以嵌套,但是不能嵌套自己
  2. 其成员都要有明确类型
  3. 成员是被正整数编号过的,其中的编号使不能重复的,这个是为了在传输过程中编码使用
  4. 成员分割符可以是逗号(,)或是分号(;),而且可以混用,但是为了清晰起见,建议在定义中只使用一种,比如 Java/C++ 学习者可以使用分号(;)
  5. 字段会有 optionalrequired 之分,和 protobuf 一样,但是如果不指定则为无类型,即可以不填充该值,但是在序列化传输的时候也会序列化进去,optional 是不填充则不序列化,required 是必须填充也一定会序列化
  6. 每个字段可以设置默认值
  7. 同一文件可以定义多个 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 标识域有缺省值或者用户已经重新赋值,而不设置它的 __issettrue,也不会被序列化传输。

五、 异常

异常在语法和功能上类似于(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 服务模型和序列化机制深入学习》

如果觉得这对你有用,请随意赞赏,给与作者支持
评论 0
最新评论