`

Thrift使用指南

 
阅读更多

1. 内容概要

本文档比较全面的介绍了thrift(关于thrift框架的介绍,参考我这篇文章:Thrift框架介绍 )语法,代码生成结构和应用经验。本文主要讲述的对象是thrift文件,并未涉及其client和server的编写方法(关于client和server的编写方法,可参考我这篇文章:使用Thrift RPC编写程序 )。

本文档大部分内容翻译自文章:Thrift:The missing Guide

2. 语法参考

2.1 Types

Thrift类型系统包括预定义基本类型,用户自定义结构体,容器类型,异常和服务定义

(1) 基本类型

1
2
3
4
5
6
7
8
9
10
11
12
13
14
bool :布尔类型( true or value),占一个字节
   
byte:有符号字节
   
i16:16位有符号整型
   
i32:32位有符号整型
   
i64:64位有符号整型
   
double :64位浮点数
   
string:未知编码或者二进制的字符串

注意,thrift不支持无符号整型,因为很多目标语言不存在无符号整型(如java)。

(2) 容器类型

Thrift容器与类型密切相关,它与当前流行编程语言提供的容器类型相对应,采用java泛型风格表示的。Thrift提供了3种容器类型:

List<t1>:一系列t1类型的元素组成的有序表,元素可以重复

Set<t1>:一系列t1类型的元素组成的无序表,元素唯一

Map<t1,t2>:key/value对(key的类型是t1且key唯一,value类型是t2)。

容器中的元素类型可以是除了service意外的任何合法thrift类型(包括结构体和异常)。

(3)  结构体和异常

Thrift结构体在概念上同C语言结构体类型—-一种将相关属性聚集(封装)在一起的方式。在面向对象语言中,thrift结构体被转换成类。

异常在语法和功能上类似于结构体,只不过异常使用关键字exception而不是struct关键字声明。但它在语义上不同于结构体—当定义一个RPC服务时,开发者可能需要声明一个远程方法抛出一个异常。

结构体和异常的声明将在下一节介绍。

(4)  服务

服务的定义方法在语法上等同于面向对象语言中定义接口。Thrift编译器会产生实现这些接口的client和server桩。具体参见下一节。

(5)  类型定义

Thrift支持C/C++风格的typedef:

1
2
3
4
typedef i32 MyInteger   \\a
   
typedef Tweet ReTweet  \\b

说明:

a.  末尾没有逗号

b.   struct可以使用typedef

2.2   枚举类型

可以像C/C++那样定义枚举类型,如:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
enum TweetType {
   
TWEET,       //a
   
RETWEET = 2, //b
   
DM = 0xa,  //c
   
REPLY
   
}        //d
   
struct Tweet {
   
1: required i32 userId;
   
2: required string userName;
   
3: required string text;
   
4: optional Location loc;
   
5: optional TweetType tweetType = TweetType.TWEET // e
   
16: optional string language = "english"
   
}

说明:

a.  编译器默认从0开始赋值

b.  可以赋予某个常量某个整数

c.  允许常量是十六进制整数

d.  末尾没有逗号

e.  给常量赋缺省值时,使用常量的全称

注意,不同于protocal buffer,thrift不支持枚举类嵌套,枚举常量必须是32位的正整数

2.3   注释

Thrfit支持shell注释风格,C/C++语言中单行或者多行注释风格

1
2
3
4
5
6
7
8
9
10
11
12
# 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.

2.4   命名空间

Thrift中的命名空间同C++中的namespace和java中的package类似,它们均提供了一种组织(隔离)代码的方式。因为每种语言均有自己的命名空间定义方式(如python中有module),thrift允许开发者针对特定语言定义namespace:

1
2
3
4
namespace cpp com.example.project  // a
   
namespace java com.example.project // b

说明:

a.  转化成namespace com { namespace example { namespace project {

b.  转换成package com.example.project

2.5   文件包含

Thrift允许thrift文件包含,用户需要使用thrift文件名作为前缀访问被包含的对象,如:

1
2
3
4
5
6
7
8
9
10
include "tweet.thrift"            // a
   
...
   
struct TweetSearchResult {
   
1: list<tweet.Tweet> tweets; // b
   
}

说明:

a.  thrift文件名要用双引号包含,末尾没有逗号或者分号

b.  注意tweet前缀

2.6   常量

Thrift允许用户定义常量,复杂的类型和结构体可使用JSON形式表示。

1
2
3
4
const i32 INT_CONST = 1234;    // a
   
const map<string,string> MAP_CONST = { "hello" : "world" , "goodnight" : "moon" }

说明:

a.  分号是可选的,可有可无;支持十六进制赋值。

2.7   定义结构体

结构体由一系列域组成,每个域有唯一整数标识符,类型,名字和可选的缺省参数组成。如:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
struct Tweet {
   
1: required i32 userId;                  // a
   
2: required string userName;             // b
   
3: required string text;
   
4: optional Location loc;                // c
   
16: optional string language = "english" // d
   
}
   
struct Location {                            // e
   
1: required double latitude;
   
2: required double longitude;
   
}

说明:

a.  每个域有一个唯一的,正整数标识符

b.  每个域可以标识为required或者optional(也可以不注明)

c.  结构体可以包含其他结构体

d.  域可以有缺省值

e.  一个thrift中可定义多个结构体,并存在引用关系

规范的struct定义中的每个域均会使用required或者optional关键字进行标识。如果required标识的域没有赋值,thrift将给予提示。如果optional标识的域没有赋值,该域将不会被序列化传输。如果某个optional标识域有缺省值而用户没有重新赋值,则该域的值一直为缺省值。

与service不同,结构体不支持继承,即,一个结构体不能继承另一个结构体。

2.8   定义服务

在流行的序列化/反序列化框架(如protocal buffer)中,thrift是少有的提供多语言间RPC服务的框架。

Thrift编译器会根据选择的目标语言为server产生服务接口代码,为client产生桩代码。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
//“Twitter”与“{”之间需要有空格!!!
service Twitter {
   
// 方法定义方式类似于C语言中的方式,它有一个返回值,一系列参数和可选的异常
   
// 列表. 注意,参数列表和异常列表定义方式与结构体中域定义方式一致.
   
void ping(),                                    // a
   
bool postTweet(1:Tweet tweet);                  // b
   
TweetSearchResult searchTweets(1:string query); // c
   
// ”oneway”标识符表示client发出请求后不必等待回复(非阻塞)直接进行下面的操作,
   
// ”oneway”方法的返回值必须是void
   
oneway void zip()                               // d
   
}

说明:

a. 函数定义可以使用逗号或者分号标识结束

b. 参数可以是基本类型或者结构体,参数是只读的(const),不可以作为返回值!!!

c. 返回值可以是基本类型或者结构体

d. 返回值可以是void

注意,函数中参数列表的定义方式与struct完全一样

Service支持继承,一个service可使用extends关键字继承另一个service

3.  产生代码

本节介绍thrift产生各种目标语言代码的方式。本节从几个基本概念开始,逐步引导开发者了解产生的代码是怎么样组织的,进而帮助开发者更快地明白thrift的使用方法。

概念

Thrift的网络栈如下所示:

3.1   Transport

Transport层提供了一个简单的网络读写抽象层。这使得thrift底层的transport从系统其它部分(如:序列化/反序列化)解耦。以下是一些Transport接口提供的方法:

1
2
3
4
5
6
7
8
9
10
open
   
close
   
read
   
write
   
flush

除了以上几个接口,Thrift使用ServerTransport接口接受或者创建原始transport对象。正如名字暗示的那样,ServerTransport用在server端,为到来的连接创建Transport对象。

1
2
3
4
5
6
7
8
open
   
listen
   
accept
   
close

3.2   Protocal

Protocal抽象层定义了一种将内存中数据结构映射成可传输格式的机制。换句话说,Protocal定义了datatype怎样使用底层的Transport对自己进行编解码。因此,protocal的实现要给出编码机制并负责对数据进行序列化。

Protocal接口的定义如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
writeMessageBegin(name, type, seq)
   
writeMessageEnd()
   
writeStructBegin(name)
   
writeStructEnd()
   
writeFieldBegin(name, type, id)
   
writeFieldEnd()
   
writeFieldStop()
   
writeMapBegin(ktype, vtype, size)
   
writeMapEnd()
   
writeListBegin(etype, size)
   
writeListEnd()
   
writeSetBegin(etype, size)
   
writeSetEnd()
   
writeBool( bool )
   
writeByte(byte)
   
writeI16(i16)
   
writeI32(i32)
   
writeI64(i64)
   
writeDouble( double )
   
writeString(string)
   
name, type, seq = readMessageBegin()
   
readMessageEnd()
   
name = readStructBegin()
   
readStructEnd()
   
name, type, id = readFieldBegin()
   
readFieldEnd()
   
k, v, size = readMapBegin()
   
readMapEnd()
   
etype, size = readListBegin()
   
readListEnd()
   
etype, size = readSetBegin()
   
readSetEnd()
   
bool = readBool()
   
byte = readByte()
   
i16 = readI16()
   
i32 = readI32()
   
i64 = readI64()
   
double = readDouble()
   
string = readString()

下面是一些对大部分thrift支持的语言均可用的protocal:

(1)     binary:简单的二进制编码

(2)     Compact:具体见THRIFT-11

(3)     Json

3.3   Processor

Processor封装了从输入数据流中读数据和向数据数据流中写数据的操作。读写数据流用Protocal对象表示。Processor的结构体非常简单:

1
2
3
4
5
6
interface TProcessor {
   
bool process(TProtocol in, TProtocol out) throws TException
   
}

与服务相关的processor实现由编译器产生。Processor主要工作流程如下:从连接中读取数据(使用输入protocol),将处理授权给handler(由用户实现),最后将结果写到连接上(使用输出protocol)。

3.4   Server

Server将以上所有特性集成在一起:

(1)  创建一个transport对象

(2)  为transport对象创建输入输出protocol

(3)  基于输入输出protocol创建processor

(4)  等待连接请求并将之交给processor处理

3.5   应用举例

下面,我们讨论thrift文件产生的特定语言代码。下面给出thrift文件描述:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
namespace cpp thrift.example
   
namespace java thrift.example
   
enum TweetType {
   
TWEET,
   
RETWEET = 2,
   
DM = 0xa,
   
REPLY
   
}
   
struct Location {
   
1: required double latitude;
   
2: required double longitude;
   
}
   
struct Tweet {
   
1: required i32 userId;
   
2: required string userName;
   
3: required string text;
   
4: optional Location loc;
   
5: optional TweetType tweetType = TweetType.TWEET;
   
16: optional string language = "english" ;
   
}
   
typedef list<Tweet> TweetList
   
struct TweetSearchResult {
   
1: TweetList tweets;
   
}
   
const i32 MAX_RESULTS = 100;
   
service Twitter {
   
void ping(),
   
bool postTweet(1:Tweet tweet);
   
TweetSearchResult searchTweets(1:string query);
   
oneway void zip()
   
}

(1) Java语言

(a)  产生的文件

一个单独的文件(Constants.java)包含所有的常量定义。

每个结构体,枚举或者服务各占一个文件

$ tree gen-java

`– thrift

`– example

|– Constants.java

|– Location.java

|– Tweet.java

|– TweetSearchResult.java

|– TweetType.java

`– Twitter.java

(b)  类型

thrift将各种基本类型和容器类型映射成java类型:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
bool : boolean
   
byte: byte
   
i16: short
   
i32: int
   
i64: long
   
double : double
   
string: String
   
list<t1>: List<t1>
   
set<t1>: Set<t1>
   
map<t1,t2>: Map<t1, t2>

(c)  typedef

Java不支持typedef,它只使用原始类型,如,在上面的例子中,产生的代码中,TweetSearchResult会被还原成list<Tweet> tweets

(d)  Enum

Thrift直接将枚举类型映射成java的枚举类型。用户可以使用geValue方法获取枚举常量的值。此外,编译器会产生一个findByValue方法获取枚举对应的数值。

(e)  常量

Thrift把所有的常量放在一个叫Constants的public类中,每个常量修饰符是public static final。

(2)  C++语言

(a)  产生的文件

所有变量均存放在一个.cpp/.h文件对中

所有的类型定义(枚举或者结构体)存放到另一个.cpp/.h文件对中

每一个service有自己的.cpp/.h文件

$ tree gen-cpp

|– example_constants.cpp

|– example_constants.h

|– example_types.cpp

|– example_types.h

|– Twitter.cpp

|– Twitter.h

`– Twitter_server.skeleton.cpp

其他语言

Python,Ruby,javascript等

4.  实践经验

thrift文件内容可能会随着时间变化的。如果已经存在的消息类型不再符合设计要求,比如,新的设计要在message格式中添加一个额外字段,但你仍想使用以前的thrift文件产生的处理代码。如果想要达到这个目的,只需:

(1)  不要修改已存在域的整数编号

(2)  新添加的域必须是optional的,以便格式兼容。对于一些语言,如果要为optional的字段赋值,需要特殊处理,比如对于C++语言,要为

1
2
3
4
5
6
7
8
9
10
struct Example{
   
1 : i32 id,
   
2 : string name,
   
3 : optional age,
   
}

中的optional字段age赋值,需要将它的__isset值设为true, 这样才能序列化并传输或者存储(不然optional字段被认为不存在,不会被传输或者存储),

如:

1
2
3
4
5
6
7
8
9
10
Example example;
   
......
   
example.age=10,
   
example.__isset.age = true ; //__isset是每个thrift对象的自带的public成员,来指定optional字段是否启用并赋值。
   
......

(3)  非required域可以删除,前提是它的整数编号不会被其他域使用。对于删除的字段,名字前面可添加“OBSOLETE_”以防止其他字段使用它的整数编号。

(4) thrift文件应该是unix格式的(windows下的换行符与unix不同,可能会导致你的程序编译不过),如果是在window下编写的,可使用dos2unix转化为unix格式。

(5)  貌似当前的thrift版本(0.6.1)不支持常量表达式的定义(如 const i32 DAY = 24 * 60 * 60),这可能是考虑到不同语言,运算符不尽相同。

分享到:
评论

相关推荐

    the programmer's guide to apache thrift

    Apache Thrift is an open source cross language serialization and RPC framework. With support for over 15 programming languages, Apache Thrift can play an important role in a range of distributed ...

    ThriftBook:《 Apache Thrift程序员指南》中示例的源代码

    Apache Thrift程序员指南以下示例中的源代码:Apache Thrift程序员指南 本书分为三个部分:第一部分-Apache Thrift概述对Apache Thrift及其体系结构的高级介绍。 这部分的例子非常有趣。 本部分还介绍了基本的Apache...

    Thrift下java服务器与客户端开发指南[归纳].pdf

    《Thrift下Java服务器与客户端开发指南》 Thrift是一种跨语言的服务开发框架,由Facebook开发,后来成为Apache基金会的开源项目。它主要用于构建高效、可扩展的分布式系统。Thrift通过定义一种中间语言(IDL,...

    thrift入门

    Thrift 入门指南 Thrift 是一种高性能的通讯中间件,支持多种编程语言,可以实现跨语言和跨平台的数据交换和 RPC 服务。Thrift 由 Facebook 开源,于 2007 年提交 Apache 软件基金会。Thrift 可以支持多种编程语言...

    thrift环境搭建(内附thrift运行环境可执行程序、搭建说明文本)

    Thrift是一种开源的跨语言服务开发框架,由Facebook于2007年创建,并于2008年贡献给了Apache基金会。...提供的“thrift环境搭建”压缩包应该包含了必要的可执行程序和详细的搭建指南,帮助你快速开始Thrift之旅。

    Thrift--JSClient

    标题中的“Thrift--JSClient”指的是Apache Thrift在JavaScript客户端的使用。Apache Thrift是一种软件框架,用于构建跨...总的来说,这篇博客应该是一个关于使用Apache Thrift进行JavaScript客户端开发的综合指南。

    thrift java build jar

    访问 Thrift 官方网站(https://thrift.apache.org/)获取最新版本,并按照平台指南进行安装。通常,这涉及到下载源码、编译并安装到系统路径中。 2. **创建 IDL 文件** Thrift 服务定义是通过 `.thrift` 文件完成...

    unity3d thrift twisted

    3. `README.md`:这是一个包含项目简介、安装指南、使用方法等内容的文档,对于理解项目至关重要。 4. `unity3d`:这个目录可能包含了Unity3D项目的所有资源和源代码,包括场景、脚本、纹理、音频等。 5. `thrift`:...

    thrift 0.9.2

    - 文档:详细说明如何使用 Thrift,包括安装指南、API 参考和示例代码。 - 测试用例:用于验证 Thrift 的功能和性能。 通过这个压缩包,你可以开始学习和使用 Thrift 来构建自己的跨语言服务,体验其强大的序列化...

    thrift-0.9.1

    这份文档可能包括用户指南、API参考以及开发者文档,帮助开发者更好地理解和应用Thrift。 5. **测试**:Thrift的源码包可能还包括测试代码,用于验证生成的代码是否正确,并确保不同语言之间的互操作性。 使用...

    thrift-0.9.0.tar.gz

    Thrift-0.9.0是这个框架的一个版本,包含源代码,用户可以编译并根据需要在自己的项目中使用。 在Thrift的实现中,它首先定义了一种中间表示(IDL,Interface Description Language),允许开发者描述服务接口、...

    Thrift下java服务器与客户端开发指南.pdf

    《Thrift下Java服务器与客户端开发指南》是指导开发者如何使用Thrift框架进行Java应用程序的服务器和客户端构建的文档。Thrift是一种开源的跨语言服务开发框架,它通过定义一个中间描述文件,可以生成多种编程语言的...

    thrift-0.10.0

    5. 文档改进:新版本通常会伴随着文档的更新和改进,提供更详尽的使用指南和示例,帮助开发者更好地理解和使用Thrift。 6. 社区支持:随着版本的更新,社区的活跃度和问题解答资源也会相应增加,用户可以更容易地...

    Thrift下java服务器与客户端开发指南【精选文档】.doc

    Thrift下java服务器与客户端开发指南【精选文档】.doc

    Thrift 0.9.1修正

    在Thrift 0.9.1这个版本中,可能存在一些编译问题,这可能会影响开发者在不同平台上构建和使用Thrift的服务。编译问题通常包括但不限于依赖库不兼容、源码错误、缺少编译选项或配置问题等。这些问题可能导致编译失败...

    Thrift下java服务器与客户端开发指南

    Thrift 的设计目标是高效、可扩展和易于使用,它支持多种编程语言,包括 Java、C++、Python、PHP 等。 【创建 Thrift 文件】 在 Thift 开发中,首先需要编写一个 `.thrift` 文件,定义服务和数据类型。例如 `...

    maven-thrift-plugin:将 thrift 文件生成为源代码的 Maven Thrift 插件

    欢迎使用 Maven Thrift 插件 Maven Thrift Plugin 用于编译你项目的 thrift 文件。 注意:确保已安装 Thrift。 有关 Thrift 安装指南,请参阅: 目标概述 generate-java绑定到 generate-sources 阶段,用于编译 ...

    Thrift下java服务器与客户端开发指南文.pdf

    《Thrift下Java服务器与客户端开发指南》 Thrift是一种高性能、可扩展的跨语言服务开发框架,由Facebook开发并开源。它通过定义一个中间表示(IDL,Interface Description Language)来描述服务,允许开发者在不同...

Global site tag (gtag.js) - Google Analytics