Thrift简介及语法

本文介绍thrift如何定义及编写接口描述文档*.thrift


1. 简介

是什么?

Thrift是Facebook开发的软件库和代码生成工具,可用于加速高效可扩展的后台服务的开发实现[1]

为什么?

随着流量和网络结构的扩展,许多操作(即搜索、广告选择和交付、事件记录)的资源需求已经远远超出了LAMP框架(Linux、Apache、MySQL和PHP或3P的简称,)的范围。实现这些服务的过程中,我们选择了各种编程语言来优化性能、开发的简便性和速度、现有库的可用性等方面的最佳组合。简单来说,选择最好的工具和实现,而不是标准化任何一种编程语言。

Thirift在多种编程语言之间构建一个透明的、高性能的桥梁。而其他解决方案不能支持足够的数据类型

目标

通过将每一种语言中最需要定制的部分抽象到用每一种语言实现的公共库中,从而实现跨编程语言的高效可靠的通信。Thrift允许开发人员在一个独立于语言的文件中定义数据类型服务接口,并生成构建RPC客户机和服务器所需的所有代码。

做什么?

为描述不同网络环境的跨语言借口,定义了一些组件。

  • 类型
  • 传输
  • 协议
  • 版本控制
  • 处理器

2. 语法

2.1 头部

头文件可以包含thrift文件c++头文件,以及命名空间声明[2][3]

2.1.1 thrift头文件

include使另一个文件中的所有符号都可见(带有前缀),并将相应的include语句添加到为这个Thrift文档生成的代码中。
例如编写了shared.thrift,可以在tutorial.thrift文件中包含。注意在include前没有#

 include "shared.thrfit"

2.1.2 C++头文件

c++ include将自定义c++ include添加到此Thrift文档的c++代码生成器的输出中。

cpp_include "test.h"

2.1.3 namespace

namespace字段声明的命名空间(namespace)、包(package)、模块(models)等。thrift允许开发者针对特定语言定义namespace。对于所有目标语言使用命名空间用*表示。

namespace cl tutorial
namespace cpp tutorial
namespace d tutorial
namespace dart tutorial
namespace java tutorial
namespace php tutorial
namespace perl tutorial
namespace haxe tutorial
namespace netstd tutorial

2.2 定义标识符

包含

  • Const
  • Typedef
  • Enum
  • Senum :弃用
  • Struct
  • Union
  • Exception
  • Service

2.2.1 const :常量

// 语法格式:const FieldType Identifier = ConstValue ListSeparator?
const i32 INT32CONSTANT = 9853

2.2.2 typedef:类型更名

// 语法格式:typedef DefinitionType Identifier
typedef i32 MyInteger

2.2.3 enum:枚举

  • 如果没有提供常量值,则第一个元素的值为0,后续任何元素的值都大于前一个值。提供的任何常数值必须是非负的。
  • 注意所有包含{}的,Identifier都与{}之间有个空格
// 语法格式:enum Identifier { (Identifier (= IntConstant)? ListSeparator?)* }
enum Operation {
  ADD = 1,
  SUBTRACT = 2,
  MULTIPLY = 3,
  DIVIDE = 4
}

2.2.4 struct:结构体

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

  • 如果required标识的字段没有赋值,thrift将给予提示。

  • 如果optional标识的字段没有赋值,该字段将不会被序列化传输。如果某个optional标识字段有缺省值而用户没有重新赋值,则该字段的值一直为缺省值。

// 语法格式:struct Identifier xsd_all? { Field* }
struct Work {
  1: i32 num1 = 0,
  2: i32 num2,
  3: Operation op,
  4: optional string comment,
}

2.2.5 union:共用体

union与struct类似,就像c++中的union{}[4]一样,所有成员占用同一段内存,修改一个成员会影响其余所有成员。因此,union成员被隐式地认为是可选的(参见requireness)。

// 语法格式:union Identifier xsd_all? { Field* }
union Sample {
  1: map<string, string> u1,
  2: binary u2,
  3: list<string> u3
}

2.2.6 exception:异常

异常类似于struct,只是异常使用关键字exception而不是struct关键字来声明,在语义上当定义一个RPC服务时,开发者可能需要声明一个远程方法抛出一个异常。每个字段的名称在异常内必须是唯一的。

// 语法格式:exception Identifier { Field* }
exception InvalidOperation {
  1: i32 what,
  2: string why
}

2.2.7 service:服务

service为thrift服务器一组函数提供接口。接口只是一个函数列表。一个服务可以扩展另一个服务,这仅仅意味着它除了提供自己的服务外,还提供扩展服务的功能。

  • ”oneway”标识符表示client发出请求后不必等待回复(非阻塞)直接进行下面的操作
  • ”oneway”方法的返回值必须是void
  • 参数可以是基本类型或者结构体,参数只能是只读的(const),不可以作为返回值
// 语法格式:service Identifier ( extends Identifier )? { Function* }
service SharedService {
  SharedStruct getStruct(1: i32 key)
}

service Calculator extends shared.SharedService {
   void ping(),
   i32 add(1:i32 num1, 2:i32 num2),
   i32 calculate(1:i32 logid, 2:Work w) throws (1:InvalidOperation ouch),
   oneway void zip()
}

2.3 注释

Thrift支持shell注释风格、C/C++语言中的单行或多行注释风格

2.4 字段

  • Field ID
  • Field Requiredness

2.4.1 field id: 字段id

  • 整数,常数
// 语法格式:IntConstant :
struct Work {
  1: i32 num1 = 0,
  2: i32 num2,
  3: Operation op,
  4: optional string comment,
}

2.4.2 字段请求状况

  • required

  • write:必填项总是被写,并且被期望被设置。

  • read:必填项字段总是被读取的,并且被期望包含在输入流中。

  • 默认值:总是写入

  • optional

  • 写:可选字段只有在设置时才写

  • 读:可选字段可能是输入流的一部分,也可能不是。

  • 默认值:在设置isset标志时写入

2.5 函数

像C代码。它有一个返回类型、参数和可选的可能抛出的异常列表。注意,参数列表和异常列表是使用与结构或异常定义中的字段列表完全相同的语法。

  • oneway:标识符表示client发出请求后不必等待回复(非阻塞)直接进行下面的操作,返回值必须是void
  • throws:异常抛出列表。参数为exception类型
   i32 calculate(1:i32 logid, 2:Work w) throws (1:InvalidOperation ouch),
   oneway void zip()

2.6 数据类型

数据类型包含基础数据类型容器类型

2.6.1 基础数据类型

  • bool
  • byte
  • i8 / i16 / i32 / i64(很少有平台能支持64位整数哦,一般都是4字节(32位)整数,数字表示位数,注意没有无符号型)
  • double
  • string
  • binary
  • slist:弃用了

2.6.2 容器类型

  • 容器(字典):map CppType? < FieldType , FieldType >
  • 集合:set CppType? < FieldType >
  • 列表:list < FieldType > CppType?

2.7 基础定义规范

  • 命名:可以包含下划线/ 点/ 短线 / 数字 / 字母 ‘.’ | ‘_’ | ‘-’
  • 分隔:逗号或分号(英文) ‘,’ | ‘;’
  • 字母:[‘A’-‘Z’] | [‘a’-‘z’]
  • 数字:[‘0’-‘9’]

2.8 示例

namespace * Skiptest.Two

const i32 SKIPTESTSERVICE_VERSION = 2

enum PingPongEnum {
	PingOne = 0,
	PongOne = 1,
	PingTwo = 2,
	PongTwo = 3,
}

struct Pong {
  1 : optional i32 version1
  2 : optional i16 version2
  100 : PingPongEnum EnumTest
}

struct Ping {
  1 : optional i32 version1
  10 : optional bool boolVal
  11 : optional byte byteVal
  12 : optional double dbVal
  13 : optional i16 i16Val
  14 : optional i32 i32Val
  15 : optional i64 i64Val
  16 : optional string strVal
  17 : optional Pong structVal
  18 : optional map< list< Pong>, set< string>> mapVal
  100 : PingPongEnum EnumTest
}

exception PingFailed {
  1 : optional i32 pingErrorCode
}

exception PongFailed {
  222 : optional i32 pongErrorCode
  10 : optional bool boolVal
  11 : optional byte byteVal
  12 : optional double dbVal
  13 : optional i16 i16Val
  14 : optional i32 i32Val
  15 : optional i64 i64Val
  16 : optional string strVal
  17 : optional Pong structVal
  18 : optional map< list< Pong>, set< string>> mapVal
}


service SkipTestService {
  Ping PingPong( 1: Ping ping, 3: Pong pong) throws (1: PingFailed pif, 444: PongFailed pof);
}

3. 传输

生成的代码使用传输层来促进数据传输。

3.1 接口

Thrift实现中的一个关键设计选择是将传输层与代码生成层解耦。虽然Thrift通常用在TCP/IP栈的顶部,流套接字作为通信的底层,但是没有必要将这种约束构建到系统中。与实际的I/O操作(通常调用系统调用)的成本相比,抽象的I/O层(每个操作有一个虚拟方法查找/函数调用)带来的性能权衡是微不足道的。

通常,生成的Thrift代码只需要知道如何读写数据。数据源和目的是不相关的;它可以是套接字、共享内存段或本地磁盘上的文件。Thrift传输接口支持以下方法:

  • open: 打开传输
  • close:关闭传输
  • isOpen:传输是否打开
  • read:从传输中读入
  • write:写到传输
  • flush:刷新

除了上述的TTransport接口,还有TServerTransport接口用来接收或创建私有传输对象。这些接口包括:

  • open:打开传输
  • listen:开始监听连接
  • accept:返回一个新的客户端传输
  • close:关闭传输

3.2 实现

传输接口为各种编程语言都设计了简单实现。一个新的传输机制可以根据需求轻松地定义。

  • TSocket
    TSocket类是跨所有目标语言实现的。它为TCP/IP流套接字提供了一个通用的、简单的接口。
  • TFileTransport
    TFileTransport是将磁盘上的文件抽象为数据流。它可以用来将一组传入的Thrift请求写到磁盘上的一个文件中。然后,可以从日志中恢复磁盘上的数据,以进行后处理。
  • 通用
    传输接口的设计目的是使用常见的OOP技术[5](如组合)支持简单的扩展。如BufferedTransport,缓冲区写入和读。TFramedTransport,传送数据帧大小头的分块优化或非阻塞操作。TMemoryBuffer,它允许直接从堆或堆栈内存读写所拥有的过程。

4. 协议

Thrift的第二个主要抽象是分离数据结构和传输表示

Thrift定义了传输数据结构,但是对于协议编码来说这个结构是模糊的。也就是说,无论数据是XML,还是ASCII,或者二进制格式编码都不重要。只要数据支持一组固定的操作,使生成的代码能够确定地读写数据。

4.1 接口

Thrift的协议接口十分重要。它支持两件事情:

  • 双向测序信息
  • 基础类型、容器和结构的编码
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, type, id = readStructBegin()
				 readStructEnd()
name = 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()

4.2 结构

Thrift结构的设计用来支持流协议编码。


5. 版本控制

在版本控制和数据定义更改的情况下,Thrift是健壮的。这对于支持对已部署的服务进行分阶段的更改非常重要。系统必须能够支持从日志文件中读取旧数据,以及从过期客户端到新服务器的请求,反之亦然。

版本控制通过field identifiers实现。

当遇到unexpected字段时,可以安全地忽略它并丢弃它。当未找到预期字段时,必须通过某种方式向开发人员发出信号,表明该字段不存在。这是通过定义对象内部的isset结构实现的。


6. RPC实现


参考

[1] Thrift interface description language

[2] Thrift: Scalable Cross-Language Services Implementation

[3] Thrift语法参考

[4] C语言共用体(C语言union用法)详解

[5] 面向对象程序设计

  • 3
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
Thrift is a framework for building cross-language services. It allows you to define data types and service interfaces in a simple definition file, and then generate code in various languages to implement those interfaces. In Python, you can use the Thrift library to create both client and server applications. To use Thrift in Python, you first need to install the Thrift library. You can do this using pip: ``` pip install thrift ``` Once installed, you can start using Thrift in your Python code. Here's a simple example to get you started: 1. Define your data types and service interface in a Thrift IDL file (e.g., `example.thrift`): ``` namespace py example struct MyStruct { 1: required string name 2: optional i32 age } service MyService { MyStruct getStruct(1: string id) } ``` 2. Generate Python code from the IDL file using the `thrift` compiler: ``` thrift --gen py example.thrift ``` 3. Implement the service interface in Python: ```python from example import MyService, ttypes class MyHandler: def getStruct(self, id): # Implementation code goes here return ttypes.MyStruct(name="John", age=25) handler = MyHandler() processor = MyService.Processor(handler) # Run the server transport = TSocket.TServerSocket(port=9090) tfactory = TTransport.TBufferedTransportFactory() pfactory = TBinaryProtocol.TBinaryProtocolFactory() server = TServer.TSimpleServer(processor, transport, tfactory, pfactory) server.serve() ``` 4. Create a client to interact with the server: ```python from example import MyService, ttypes transport = TSocket.TSocket("localhost", 9090) transport = TTransport.TBufferedTransport(transport) protocol = TBinaryProtocol.TBinaryProtocol(transport) client = MyService.Client(protocol) transport.open() struct = client.getStruct("123") print(struct.name) print(struct.age) transport.close() ``` This is just a basic example to give you an idea of how to use Thrift with Python. You can find more details and advanced usage in the Thrift documentation.

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值