thrift的基本语法和使用以及相关坑的介绍
Thrift包含一套完整的栈来创建客户端和服务端程序。[8]顶层部分是由Thrift定义生成的代码。而服务则由这个文件客户端和处理器代码生成。在生成的代码里会创建不同于内建类型的数据结构,并将其作为结果发送。协议和传输层是运行时库的一部分。有了Thrift,就可以定义一个服务或改变通讯和传输协议,而无需重新编译代码。除了客户端部分之外,Thrift还包括服务器基础设施来集成协议和传输,如阻塞、非阻塞及多线程服务器。栈中作为I/O基础的部分对于不同的语言则有不同的实现。
thrift一般用于内部服务之间的通讯,因为thrift支持多种协议的编译,并且是通过idl文件的形式严格声明变量的各个字段和接口的格式,内部服务的客户端只需要使用thrift文件生成的stub代码即可完成调用,不需要去关心业务各个字段的关系,也不需要像对待json那样需要自己序列化并且可能面临数据格式的更改导致业务逻辑的异常等,thrift的字段可以通过新增字段去保持老版本的成功调用,因此升级可能不需要面临格式变更导致也需要调用方做相关修改。
安装thrift
mac
brew install thrift
复制代码
linux
需要从git拉下来thrift的源代码,然后根据官方文档进行cmake的编译 thrift.apache.org/docs/Buildi…
thrift基本语法
thrift有多种基本类型
bool
true 或者 flase
byte
8bit编码 等价于i8
i16
int16
i32
int32
i64
int64
double
64位浮点数
string
utf-8编码的字符串
我们声明一个请求体或者一个结构体(类)可以使用以下语法去声明
demo.thrift
struct demoRequest {
1: string request_text = "request for demo";
2: i32 id;
3: i8 reason = 0; // 指定默认值
4: double count;
}
复制代码
执行编译查看一下编译后的结果,这里我们编译成go
thrift --gen go -r demo.thrift
复制代码
type DemoRequest struct {
RequestText string `thrift:"request_text,1" db:"request_text" json:"request_text"`
ID int32 `thrift:"id,2" db:"id" json:"id"`
Reason int8 `thrift:"reason,3" db:"reason" json:"reason"`
Count float64 `thrift:"count,4" db:"count" json:"count"`
}
func NewDemoRequest() *DemoRequest {
return &DemoRequest{
RequestText: "request for demo",
}
}
// ...
func (p *DemoRequest) ReadField1(ctx context.Context, iprot thrift.TProtocol) error {
if v, err := iprot.ReadString(ctx); err != nil {
return thrift.PrependError("error reading field 1: ", err)
} else {
p.RequestText = v
}
return nil
}
复制代码
可以看到thrift为我们自动生成了一个go文件里面是我们在thrift文件定义好的一个结构体,并且写上了一部分的编码逻辑,遇到哪个field就使用哪个编码和解码格式去进行编码和解码。field的顺序就是我们在thrift每个变量开头冒号前面的数字,这个数字可以不断新增,只要不修改原有字段的话,向下兼容,新增加的field在老版本是不会解出来的,因此使用起来会比json安全,在内部的情况下,不过当然也不会有json灵活和便于调试和可读性。
thrift的optional和require
每个变量都可以指定为optional或者require 字段都是默认Optinal的,如果thrift没有解析到require字段的话是会报错的,因此可以确保该字段不为空, 但是标注成optional和不标注成optional的话会在对应语言有可能产生不同的实现,在go里面optinal string
会变成*string
,而string
是string
,因此标注成optinal
以后可以创建一个为null的field,但是不标注optinal只能是“”
struct Response {
1: optional string msg;
2: required i64 code;
3: required map<string,string> data;
}
复制代码
重新编译一下
thrift --gen go -r demo.thrift
复制代码
if !issetCode{
return thrift.NewTProtocolExceptionWithType(thrift.INVALID_DATA, fmt.Errorf("Required field Code is not set"));
}
if !issetData{
return thrift.NewTProtocolExceptionWithType(thrift.INVALID_DATA, fmt.Errorf("Required field Data is not set"));
}
复制代码
thrift的容器类型
list
类似于c++ vector和java ArrayList等
set
类似于c++ set和java HashSet等
map
类似于c++ map和java HashMap list和set的用法和c++的模板用法很相似list<string>
, set<string>
,map<i32,double>
demo.thrift
struct demoResponse {
1: list<string> request_list;
2: set<demoRequest> response_set; //可以使用上面声明的demoRequest
3: map<i32, double> has;
}
复制代码
重新编译一下
thrift --gen go -r demo.thrift
复制代码
type DemoResponse struct {
RequestList []string `thrift:"request_list,1" db:"request_list" json:"request_list"`
ResponseSet []*DemoRequest `thrift:"response_set,2" db:"response_set" json:"response_set"`
Has map[int32]float64 `thrift:"has,3" db:"has" json:"has"`
}
复制代码
支持枚举类 用法有点需要注意的,枚举值的大小需要在int32的范围内,否则会编译不通过
enum Code {
success = 1;
error = 2;
unknown = 2147483648; // 这样会编译错误 因为int32的最大值是 2147483647
}
复制代码
thrift --gen go -r demo.thrift
复制代码
[FAILURE:/Users/thrift_demo/demo.thrift:4] 64-bit value supplied for enum unknown will be truncated.
复制代码
使用enum也是如枚举类的使用方法一样即可
struct demoRequest {
1: string request_text = "request for demo";
2: i32 id;
3: i8 reason = 0; // 指定默认值
4: double count;
5: Code code = Code.success;
}
复制代码
异常类的声明如同struct
exception SystemError{
1: string reason;
2: i64 timestamp;
}
复制代码
声明一个可被外部调用的方法
service TestSystem{
demoResponse call(1: demoRequest request) throws (1: SystemError error);
void call1(1: demoRequest request);
}
复制代码
方法可以抛出异常也可以不抛出异常,类似c++的声明的方法即可。
thrift编译的一些坑(本来确实踩了很多坑,但是最近貌似升级到0.18.1以后坑少了挺多)
- 声明的任何struct,enum,想要使用必须在使用者前面声明,不会像现在很多的高级语言一样,可以放在后面声明,必须先声明后使用,否则报错
example:
struct demoRequest {
1: string request_text = "request for demo";
2: i32 id;
3: i8 reason = 0; // 指定默认值
4: double count;
5: Code code = Code.success;
}
enum Code {
success = 1;
error = 2;
unknown = 2147483647;
}
复制代码
编译后会报错:
Type "Code" not defined
复制代码
总结
thrift提供了基本的编解码功能和点对点的通信(通信demo可以看thrift的官方文档 thrift.apache.org/tutorial/go…