Thrift基础
Thrift最初由Facebook研发,主要用于各个服务之间的RPC通信,支持跨语言, 常用的语言比如C++, Java, Python, PHP, Ruby等语言都支持。 Thrift是一个典型的CS(客户端/服务端)结构,客户端和服务端可以使用不同的语言开发。 在推荐系统进行Serving的时候,用到这个进行快速的交互。
本文是从Thrift Tutorial及网上的一些资料整理学习得到,版权归原作者。
1. Thrift中的类型
1.1 基本类型
thrift不支持无符号类型,因为很多编程语言不存在无符号类型。
- byte: 有符号字节
- i16: 16位有符号整数
- i32: 32位有符号整数
- i64: 64位有符号整数
- double: 64位浮点数
- string: 字符串
1.2 特殊类型
binary: 无编码字节序列
1.3 结构体 Structs
就像C语言一样,thrift也支持struct类型,目的就是将一些数据聚合在一起,方便传输管理。struct的定义形式如下:
1 | struct Example { |
1.4 容器类型 Containers
集合中的元素可以是除了service之外的任何类型,包括exception。
- list (类似c++ STL vector、Java ArrayList)
- set (类似STL set、Java HashSet),
PHP不支持set,所以PHP对待set就像是map
- map (类似STL map、Java HashMap)
1.5 异常 Exceptions
thrift支持自定义exception,规则和struct一样,如下:
1 | exception InvalidOperation { |
1.6 枚举 Enum
枚举的定义形式和Java的Enum定义差不多,例如:
1 | enum Sex { |
1.7 服务 Service
thrift定义服务相当于Java中创建Interface一样,创建的service经过代码生成命令之后就会生成客户端和服务端的框架代码。定义形式如下:
1 | service <name> { |
具体例子如下:
1 | service StringCache { |
2. Thrift中其他操作
2.1 类型重定义 typedef
thrift支持类似C++一样的typedef定义,比如:
1 | typedef i32 int |
NOTE: 末尾没有逗号或者分号
2.2 常量 const
thrift也支持常量定义,使用const关键字,例如:
1 | const i32 MAX_RETRIES_TIME = 10 |
NOTE: 末尾的分号是可选的,可有可无,并且支持16进制赋值
2.3 命名空间
thrift的命名空间相当于Java中的package的意思,主要目的是组织代码。thrift使用关键字namespace定义命名空间,例如:
1 | namespace java tutorial |
NOTE: 格式是:namespace 语言名 路径
,注意末尾不能有分号,不同的语言可以设置不同的namespace
2.4 文件包含
thrift也支持文件包含,相当于C/C++中的include,Java中的import。使用关键字include定义,例如:
1 | include "shared.thrift" |
2.5 注释
thrift注释方式支持shell风格的注释,支持C/C++风格的注释,即#和//开头的语句都单当做注释,/**/包裹的语句也是注释。
2.6 可选与必选
thrift提供两个关键字required
,optional
,分别用于表示对应的字段时必填的还是可选的。例如:
1 | struct People { |
表示name
是必填的,age
是可选的。
3. 实例
3.1 生成代码
知道了怎么定义thrift文件之后,我们需要用定义好的thrift文件生成我们需要的目标语言的源码,本文以生成java源码为例。
假设现在定义了如下一个thrift文件,命名为Test.thrift
:
1 | namespace java com.winwill.thrift |
在终端运行如下命令(前提是已经安装thrift):
1 | thrift --gen java Test.thrift # 生成jave的代码 |
则在当前目录会生成一个gen-java
/gen-py
/gen-cpp
目录,该目录下会按照namespace定义的路径名一次一层层生成文件夹,到gen-java/com/winwill/thrift/
目录下可以看到生成的4个Java类。
可以看到,thrift文件中定义的enum
,struct
,exception
,service
都相应地生成了一个Java类,这就是能支持Java语言的基本的框架代码。
3.2 服务端实现-对thrift中定义的service的接口实现
上面代码生成这一步已经将接口代码生成了,现在需要做的是实现HelloWordService
的具体逻辑,实现的方式就是创建一个Java类,
implements com.winwill.thrift.HelloWordService
,例如:
1 | package com.winwill.thrift; |
3.3 启动服务-Server端
上面这个就是服务端的具体实现类,现在需要启动这个服务,所以需要一个启动类,启动类的代码如下:
1 | package com.winwill.thrift; |
运行之后看到控制台的输出为:
1 | Running server... |
3.4 客户端请求-Client端
现在服务已经启动,可以通过客户端向服务端发送请求了,客户端的代码如下:
1 | package com.winwill.thrift; |
如果是Python代码的话,原生的socket太慢了,可以再包装一层buffer
1 | # Make socket |
运行客户端代码,得到结果:
1 | Hello, winwill2012, Welcome! |
并且此时,服务端会有请求日志:
1 | Running server... |
可以看到,客户端成功将请求发到了服务端,服务端成功地将请求结果返回给客户端,整个通信过程完成。