Golang架构直通车——理解Thrift

什么是Thrift

thrift是一个跨语言通信的工具,支持的语言多,而且还提供服务器端的众多网络模型,使服务端的开发可以只专于服务本身的逻辑。用户通过Thrift的IDL(接口定义语言)来描述接口函数及数据类型,然后通过Thrift的编译环境生成各种语言类型的接口文件。

简单来说,Thrift是RPC下的序列化协议和传输协议。


  • 什么是IDL?

IDL(全称:Interface Description Language,接口描述语言):
既然要进行 RPC,就需要知道对方的接口是什么,需要传什么参数,同时也需要知道返回值是什么样的。接口描述语言就定义了双方达成RPC通信的方式

Thrift工作原理

本地函数调用调用方和被调用方都在一个程序内部,只是cpu在执行调用的时候切换去执行被调用的函数,执行完被调用函数之后,再切换回来执行调用之后的代码。

远端过程调用(RPC)的调用方和被调用方不在同一个进程内,甚至都不在同一台机子上,因此远端过程调用中,必然涉及网络传输。Thrift框架的远端过程调用的工作过程如下:

  1. 通过IDL定义一个接口的thrift文件,然后通过thrift的多语言编译功能,将接口定义的thrift文件翻译成对应的语言版本的接口文件;
  2. Thrift生成的特定语言的接口文件中包括客户端部分和服务器部分;
  3. 客户端通过接口文件中的客户端部分生成一个Client对象,这个客户端对象中包含所有接口函数的存根实现,然后用户代码就可以通过这个Client对象来调用thrift文件中的那些接口函数了,但是,客户端调用接口函数时实际上调用的是接口函数的本地存根实现;
  4. 接口函数的存根实现将调用请求发送给thrift服务器端,然后thrift服务器根据调用的函数名和函数参数,调用实际的实现函数来完成具体的操作;
  5. Thrift服务器在完成处理之后,将函数的返回值发送给调用的Client对象;
  6. Thrift的Client对象将函数的返回值再交付给用户的调用函数。

RPC在调用方和被调用方一般不在一台机子上,它们之间通过网络传输进行通信,一般的RPC都是采用tcp连接,如果同一条tcp连接同一时间段只能被一个调用所独占,这种情况是同步调用。
在一些的RPC服务框架中,为了提升网络通信的效率,客户端发起调用之后不被阻塞,这种情况是异步调用,它的通信效率比同步调用高,但是实现起来比较复杂。

Thrift IDL语法

Thrift IDL语法和Protobuf语法很像,这里挑一些东西做简单介绍,具体可以参考:Thrift IDL语法


Type

  • bool: 布尔值 (true or false)
  • byte: 有符号字节
  • i16: 16位有符号整型
  • i32: 32位有符号整型
  • i64: 64位有符号整型
  • double: 64位浮点型
  • string: Encoding agnostic text or binary string
  • binary: Blob (byte array) a sequence of unencoded bytes,这是string类型的一种变形,主要是为.java使用

Struct

  1. 字段会有optional和required之分和protobuf一样,但是如果不指定则为无类型—可以不填充该值,但是在序列化传输的时候也会序列化进去,optional是不填充则不序列化,required是必须填充也必须序列化。
  2. 成员是被正整数编号过的,其中的编号是不能重复的,这个是为了在传输过程中编码使用。
  3. 每个字段可以设置默认值。
  4. 同一文件可以定义多个struct,也可以定义在不同的文件,进行include引入。
  5. 成员分割符可以是逗号(,)或是分号(;),而且可以混用,但是为了清晰期间,建议在定义中只使用一种。
struct Report
{
  1: required string msg, //序列化中,必须有该字段
  2: optional i32 type = 0; //序列化中可以没有该字段
  3: i32 time //对于基本类型,会序列化到字节数组中,解析的时候不会校验。对于对象类型,如果不为空,则序列化到字节数组中
}

Containers
Thrift容器与目前流行编程语言的容器类型相对应,有3种可用容器类型:

  1. list: 元素类型为t的有序表,容许元素重复。对应c++的vector,Java的ArrayList或者其他语言的数组
  2. set: 元素类型为t的无序表,不容许元素重复。对应c++中的set,Java中的HashSet,python中的set,php中没有set,则转换为list类型了
  3. map: 键类型为t,值类型为t的kv对,键不容许重复。对用c++中的map, Java的HashMap, PHP 对应 array, Python/Ruby 的dictionary。

注:为了最大的兼容性,map的key最好是thrift的基本类型,有些语言不支持复杂类型的key,JSON协议只支 持那些基本类型的key。

struct Test {
  1: map<Number, UserId> user_map,
  2: set<Number> num_sets,
  3: list<Stusers> users
}

Enum

enum EnOpType {
  CMD_OK = 0, // (0)
  CMD_NOT_EXIT = 2000, // (2000)
  CMD_EXIT = 2001, // (2001)
  CMD_ADD = 2002 // (2002)}
struct StUser {
  1: required i32 userId;
  2: required string userName;
  3: optional EnOpType cmd_code = EnOpType.CMD_OK;
  4: optional string language = “english”
}

Consts
Thrift允许定义跨语言使用的常量,复杂的类型和结构体可使用JSON形式表示。

const i32 INT_CONST = 1234;
const EnOpType myEnOpType = EnOpType.CMD_EXIT; //2001

Exceptions

exception UserException {
  1: i32 errorCode,
  2: string message,
  3: StUser userinfo
}

Services
服务的定义方法在语义(semantically 上等同于面向对象语言中的接口。
Thrift编译器会产生执行这些接口的client和server stub。 Thrift编译器会根据选择的目标语言为server产生服务接口代码,为client产生stubs。

service SeTest {
    void ping() throws (1:UserException e),
    bool postTweet(1: StUser user);
    StUser searchTweets(1:string name);
}

Service的约束

  1. 不支持多态
  2. 方法无法重载

Namespace
Thrift中的命名空间类似于C++中的namespace和Java中的package,它们提供了一种组织(隔离)代码的简便方式。名字空间也可以用于解决类型定义中的名字冲突。

namespace cpp com.example.test
namespace java com.example.test
namespace php com.example.test

Includes

include "test.thrift"
...
struct StSearchResult {
 1: in32 uid;
 ...
}

RPC框架对比

在这里插入图片描述

gRPC和Thrift对比可以参考:gRPC vs Thrift

  • 0
    点赞
  • 5
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值