ApacheThriftIDL中的结构、联合、异常和服务详解
立即解锁
发布时间: 2025-08-19 02:31:28 阅读量: 2 订阅数: 16 


Apache Thrift程序员指南:从入门到精通
### Apache Thrift IDL 中的结构、联合、异常和服务详解
在使用 Apache Thrift IDL 进行开发时,有许多重要的概念和特性需要我们深入了解,包括枚举常量、结构体、联合、异常、参数列表以及服务等。下面将详细介绍这些内容。
#### 枚举常量
枚举常量应该是非负整数。如果 IDL 编译器遇到负的枚举值,会产生警告,不过大多数语言生成器仍然会使用它们。最佳实践是为枚举常量显式分配正值,这样可以避免因编译器或 IDL 源文件版本变化导致的值生成问题,以及其他兼容性问题。
#### 结构、联合、异常和参数列表
结构、联合、异常和参数列表都允许将一组元素收集到一个关联组中。服务包含一组函数,每个函数都有一个由一组字段组成的参数列表。结构体、联合和异常也包含一组字段。Apache Thrift IDL 编译器内部使用相同的数据结构来表示结构体、联合、异常和参数列表中的字段集。
以下是结构体、联合、异常和参数列表的示例声明:
```
struct stTimeStamp {
1: i16 year
2: i16 month
3: i16 day
}
union unTimeStamp {
1: i16 year
2: i16 month
3: i16 day
}
exception exTimeStamp {
1: i16 year
2: i16 month
3: i16 day
}
service svTimeStamp {
void fnTimeStamp (1: i16 year
2: i16 month
3: i16 day)
}
```
字段可以是任何有效类型,包括基本类型、结构体、联合、异常和容器。上述示例中的每组字段都会被编译成具有 `read()` 和 `write()` 方法的特定语言类。`read()` 方法用于读取结构体、联合、异常或参数列表的实例,`write()` 方法用于写入这些实例。读写操作都将协议作为参数,用于从协议中读取或写入数据。
#### 结构体
结构体可能是最简单的字段分组类型,也是用户构建基于 IDL 的用户定义类型(UDT)的主要方式。结构体提供了一种方便的方法,将一组相关字段收集到一个可管理的程序元素中。例如:
```
struct TimeStamp {
1: i16 year
2: i16 month
3: i16 day
}
```
在 IDL 中定义的每个结构体都会创建一个类型,并且必须给它一个名称。上述示例中,声明的结构体类型名为 `TimeStamp`。结构体的所有重要方面都由它包含的字段描述。
#### 字段
大多数与字段相关的细节对于结构体、联合、异常和参数列表是通用的。下面以结构体为例描述字段,但这些原则同样适用于联合、异常和参数列表,除非另有说明。
字段列表采用以下形式:
```
[id:] [requiredness] type FieldName [=default] [,|;]
...
```
每个字段都有一个类型、一个名称和一个标识符(ID)。字段不能使用未定义或部分定义的类型。例如,结构体 “A” 不能包含类型为 “A” 的字段。除服务外,IDL 类型不支持继承,也不能组织成类型层次结构。
需要注意的是,目前有一个实验性的计划,旨在使 Apache Thrift IDL 支持自引用类型。例如,名为 “Node” 的结构体类型可以有 “Node &”(节点引用)类型的属性,这使得创建有向无环图(DAG),如树和单链表成为可能。但截至目前,此功能仅在 C++ 中实现,并且尚未经过大量测试。
##### 字段 ID
字段 ID(有时称为键)是 16 位整数,可以是显式的或隐式的。Thrift 框架在许多情况下使用字段 ID 来唯一标识字段。例如,在调用 RPC 函数时,参数可以按任意顺序传递,接收方将使用字段 ID 来匹配传递的参数与正确的参数。
显式字段 ID 必须是正整数。在 `TimeStamp` 示例结构体中,字段 ID 是 1、2 和 3。显式定义字段 ID 几乎总是有利的。如果没有提供显式 ID,IDL 编译器会分配隐式字段标识符,隐式 ID 是负数(从 -1 开始递减)。如果结构体中没有显式 ID 而更改字段顺序,几乎总会破坏与现有代码的兼容性,因为新顺序生成的隐式 ID 可能与之前的不同。例如,对于 `struct {i16 a, i16 b}`,IDL 编译器将分别为 `a` 和 `b` 生成 ID -1 和 -2;而对于 `struct {i16 b, i16 a}`,则会分别生成 ID -2 和 -1。
IDL 编译器提供了 `-allow-neg-keys` 开关,允许显式分配负 ID,但这仅应用于解决与依赖预定义负 ID 的现有 Apache Thrift 系统的互操作性问题。
##### 字段的必需性
每个字段都有一个必需性属性,该属性定义了 Apache Thrift 如何读写该字段。字段可以有三种必需性值:`required`、`default` 和 `optional`。此外,当程序在序列化结构体中发现一个它不知道的字段时,会出现第四种情况,这里称为 “undefined”,结构体读取器会忽略这些未定义的字段。
对于编写 IDL 结构体的程序,有三种可能的必需性场景(`required`、`default` 和 `optional`);而从序列化结构体中读取字段的程序可能会遇到四种必需性场景(前三种加上未定义字段)。以下是字段必需性的情况:
| 字段必需性 | 写入行为 | 读取行为 |
| --- | --- | --- |
| Required | 必须读取,否则报错 | 始终写入 |
| <default> | 若存在则读取 | 始终写入 |
| Optional | 若存在则读取 | 若设置则写入 |
| <undefined> | 忽略 | N/A |
考虑以下结构体:
```
struct Trade {
1: required string fish
2: double price
3: optional i32 size
}
```
这个简单的结构体展示了所有可能的 IDL 必需性值。第一个字段 `fish` 被标记为 `required`,第二个字段 `price` 没有必需性指定,默认为 `default` 必需性,第三个字段 `size` 被指定为 `optional`。
- **必需字段**:如 `fish` 字段,在结构体序列化时总是会被写入,并且在反序列化时必须被读取。如果结构体的 `read()` 方法在读取结构时找不到必需字段,将抛出 `TProtocolException`。定义必需字段时要小心,因为它们是必需性类型中灵活性最低的。例如,当你移除或添加一个必需字段时,所有通信该结构体的程序都必须更新,以避免抛出异常。
- **默认必需性字段**:如 `price` 字段,总是会被写入,但在读取操作中不必存在。这使得结构体定义可以随着时间逐步演变。例如,假设波特兰市场和西雅图市场的两个系统使用 Apache Thrift RPC 进行通信。波特兰团队需要在之前的交易结构体中添加一个 `TimeStamp` 字段,如果该字段被赋予默认必需性,波特兰系统将始终传输该字段,并且如果存在,波特兰系统会始终读取它。而当波特兰市场将交易结构
0
0
复制全文
相关推荐




