概诉
前一章节,我们基本入门,了解了 Thrift
的基本用法,这一节继续来分析 Thrift
IDL的内容。
Thrift
采用IDL(Interface Definition Language
)来定义通用的服务接口,然后通过 Thrift
提供的编译器,可以将服务接口编译成不同语言编写的代码,通过这个方式来实现跨语言的功能。
IDL定义的规范
IDL 结构规范
1 | Document ::= Header* Definition* |
IDL的结构分为Header,Definition两个部分。
Head 只是的是协议头,主要包含三个关键字
- Include
- CppInclude
- Namespace
Thrift IDL 的 Definition为定义部分。
IDL 语法规范
Identifier
官方文档中的定义如下:
Identifier ::= ( Letter | '_' ) ( Letter | Digit | '.' | '_' )*
复制代码即合法的标识符满足以下条件:
1、标识符只能由字母,数字,(under score), .(dot)组成
2、只能以字母, 开头
IDL Definition 部分
语法组成
1 |
|
FieldID 为类型的展位符号 通常用int
FieldType
关键字 | 类型 | 对应Java中的类型 |
---|---|---|
bool | 布尔值 | 对应Java中的boolean |
byte | 有符号字节 | 对应Java中的byte |
i16 | 16位有符号整型 | 对应Java中的short |
i32 | 32位有符号整型 | 对应Java中的int |
i64 | 64位有符号整型 | 对应Java中的long |
double | 64位浮点型 | 对应Java中的double |
string | 字符串 | 对应Java中的String |
binary | Blob 类型 | 对应Java中的byte[] |
slist |
可以是基础类型,容器类型或者合法标识符,这里的合法标识符就是下文中通过 typedef, enum, struct 等关键中声明的类型。
FiledReq
-
required:
- 写:必须字段始终写入,并且应该设置这些字段。
- 读:必须字段始终读取,并且它们将包含在输入流中
- 默认值:始终写入
注意:如果一个必须字段在读的时候丢失,则会抛出异常或返回错误,所以在版本控制的时候,要严格控制字段的必选和可选,必须字段如果被删或者改为可选,那将会造成版本不兼容。
-
optional:
1、写:可选字段仅在设置时写入
2、读:可选字段可能是也可能不是输入流的一部分
3、默认值:在设置了isset标志时写入Thrift使用所谓的“isset”标志来指示是否设置了特定的可选字段, 仅设置了此标志的字段会写入,相反,仅在从输入流中读取字段值时才设置该标志。
-
default:
1、写:理论上总是写入,但是有一些特例
2、读:跟optional一样
3、默认值:可能不会写入默认类型是required和optional的结合,可选输入(读),必须输出(写)。
1
2
3
4
5
6
### 例子
```java
1: required string name,
2: required i16 age = 0;
Container (容器)
有3种可用容器类型:
-
list
: 元素类型为t的有序表,容许元素重复。对应c++的vector,java的ArrayList或者其他语言的数组 -
set
: 元素类型为t的无序表,不容许元素重复。对应c++中的set,java中的HashSet,python中的set,php中没有set,则转换为list类型了 -
map<t, t>: 键类型为t,值类型为t的kv对,键不容许重复。对用c++中的map, Java的HashMap, PHP 对应 array, Python/Ruby 的dictionary
例子:
1 | 7: required list<User> friends, |
struct结构体
struct语法
1 | Struct ::= 'struct' Identifier 'xsd_all'? '{' Field* '}' |
xsd_all 是 Facebook 内部的字段,直接忽略,就算你写了,也不会有啥影响 Field 中的 XsdFeildOptions 也是 Facebook 内部字段,直接忽略
从语法定义看,一个 Struct 定义的核心是 Field
字段,而且每个字段的名字在一个 Struct
内要确保是唯一的,Struct
不能继承,但是可以嵌套使用,即可以作为 struct
字段的类型。
接下来就仔细看下 Field
的定义,一个合法的 Field
只需要哟 FieldType
和对应的 Identifier
就可以了,但是通常我们都会加上 FieldId
和 ListSeparator
, 而 FieldReq 则视情况而定。
FieldId
必须是整型常量加 : 组成。
struct约束:
- struct不能继承,但是可以嵌套,不能嵌套自己。
- 其成员都是有明确类型
- 成员是被正整数编号过的,其中的编号使不能重复的,这个是为了在传输过程中编码使用。
- 成员分割符可以是逗号(,)或是分号(;),而且可以混用
- 字段会有optional和required之分和protobuf一样,但是如果不指定则为无类型–可以不填充该值,但是在序列化传输的时候也会序列化进去,optional是不填充则部序列化,required是必须填充也必须序列化。
- 每个字段可以设置默认值
- 同一文件可以定义多个struct,也可以定义在不同的文件,进行include引入。
例子
1 | struct Friends { |
Union
语法格式
1 |
|
常量类型
语法
1 | Const ::= 'const' FieldType Identifier '=' ConstValue ListSeparator? |
例子
先说明下 ListSeparator
, 这个分隔符就好比 Java 中一句话结束后的 ;,在 IDL 中分隔符可以是 , 或者 ; 而且大部分情况下可以忽略不写
IDL 中通过 const 关键字进行声明
1 | const string testConst = 'hello,thrift'; // `;` 可以替换为 `,` 也可以不写 |
异常类型
语法
1 | ### 例子 |
枚举类型
语法:
1 | ### 例子 |
复制代码从语法定义来看,(’=’ IntConstant)? 是一个可选项,也就是说我们可以不用指定值,默认就是从 0 开始递增,如果要指定,那就必须是一个整型常量,如果后续没有再指定,则从第一个指定的整型值开始进行递增. 所以上述示例中的枚举值,也可以写成如下:
1 | enum fb_status { |
Typedef
语法
1 | ### 例子 |
Services类型
语法
上面的小节中介绍的所有内容都是用来服务 Service
的,Service
提供了我们要暴露的接口,而且,Service
是可以被继承的,Service A
继承了 Service B
, A 除了提供自己定义的接口外,他还提供了从 B 继承来的接口。语法定义如下:
1 | Service ::= 'service' Identifier ( 'extends' Identifier )? '{' Function* '}' |
复制代码从语法定义,我们可以看到 Service 的核心就是 Function
的定义
例子
1 | service ExampleService { |
oneway
是一个关键字,从字面上,我们就可以了解到,他是单向的,怎么理解呢?非 oneway
修饰的 function 是应答式,即 req-resp
, 客户端发请求,服务端返回响应,被 oneway
修饰后的函数,则意味着,客户端只是会发起,无须关注返回,服务端也不会响应,与 void
的区别是 void
类型的方法还可以返回异常。
FunctionType
是任何合法的 FieldType
或者 void
关键字,表示无返回类型
Throws
顾名思义,参考上述示例即可。
IDL Head 部分
从语法定义看,Header 可以是 Include
, CppInclude
, Namespace
, 接下来,我们依次进行介绍。
Include 语法定义
语法
1 |
|
- example.thrift
1 | include 'base.thrift' |
CppInclude
语法
复制代码CppInclude 语法定义
CppInclude ::= ‘cpp_include’ Literal
复制代码CppInclude 主要是用来为当前的 thrift 文件生成的代码中添加一个自定义的 C++ 引入声明 目前没有使用场景,不做过多陈述
Namespace
语法
1 | Namespace ::= ( 'namespace' ( NamespaceScope Identifier ) ) |
复制代码 Namespace
用来声明使用哪种语言来处理当前 thrift
文件中定义的各种类型,NamespaceScope
就是各种语言的标识,也可以指定为通配符 * 标识,标识 thrift 文件中的定义适用于所有的语言。除此之外 Namespace 还有一个作用就是避免不同 Identifier
定义的命名冲突。
例子
1 | namespace java com.facebook.fb303 |
上述示例中 Namespace
声明语句,标识当前的 thrift 文件适用于 java
NamespaceScope 后面紧跟着的 Identifier 在不同语言中会有不一样的表现.
参考
赞赏一下