hrift的基本语法和使用以及相关坑笔记

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,而stringstring,因此标注成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以后坑少了挺多)

  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…

猜你喜欢

转载自juejin.im/post/7216968724937965625