Golang 微服务框架 Kratos 实现 Thrift 服务
Golang 微服务框架 Kratos 实现 Thrift 服务
什么是 Thrift
Thrift 是 Facebook 于 2007 年开发的跨语言的 rpc 服框架,提供多语言的编译功能,并提供多种服务器工作模式;用户通过 Thrift 的 IDL(接口定义语言)来描述接口函数及数据类型,然后通过 Thrift 的编译环境生成各种语言类型的接口文件,用户可以根据自己的需要采用不同的语言开发客户端代码和服务器端代码。2007 年由 facebook 贡献到 apache 基金,是 apache 下的顶级项目,具备如下特点:
支持多语言:C、C++ 、C# 、D 、Delphi 、Erlang 、Go 、Haxe 、Haskell 、Java 、JavaScript、node.js 、OCaml 、Perl 、PHP 、Python 、Ruby 、SmallTalk
消息定义文件支持注释,数据结构与传输表现的分离,支持多种消息格式
包含完整的客户端/服务端堆栈,可快速实现 RPC,支持同步和异步通信
Thrift 的优缺点
Thrift 的优点
One-stop shop,相对于 protobuf,序列化和 RPC 支持一站式解决,如果是 pb 的话,还需要考虑选择 RPC 框架,现在 Google 是开源了 gRpc,但是几年以前是没有第一方的标准解决方案的
特性丰富,idl 层面支持 map,protobuf 应该是最近才支持的,map 的 key 支持任意类型,avro 只支持 string,序列化支持自定义 protocol, rpc 支持 thread pool, hsha, no blocking 多种形式,必有一款适合你,对于多语言的支持也非常丰富
RPC 和序列化性能都不错,这个到处都有 benchmark,并不是性能最好的,但是基本上不会成为瓶颈或者短板
有很多开源项目的周边支持都是 thrift 的,hbase 提供 thrift 服务,hive,spark sql,cassandra 等一系列对外的标准服务接口都是 thrift 的以支持多语言。
Column Storage 的话,parquet 支持直接通过 thrift idl 转换,如果在 Hadoop 集群上存储数据,elephant-bird 支持得很好,你可以很方便地针对 thrift 的数据通过 pig 写 dsl,如果你希望在 rpc 服务外做一系列工作,可以用 finagle 包装一层。不过,这部分对于 protobuf 和 avro 支持一般也不错
Thrift 的缺点
基本没有官方文档
RPC 在 0.6.1 升级到 0.7.0 是不兼容的!这个对于早于 0.6.1 开始使用的用户来说是个大坑
bug fix 和更新不积极,好在序列化和 RPC 服务都不是太复杂的问题,需要考量的设计问题不多,自己维护 patch 的成本不高,如果我没有记错的话,0.6.1 的 java 的 ThreadPool Server 是会有 Thread 死亡之后的 Thread 泄露问题的
不支持双向通道,如果要支持双向通道比较麻烦
rpc 方法非线程安全,这就是为何很多时候服务器会被挂死,是因为客户端的并发 rpc 调用导致的,只需要客户端对 rpc 的调用进行串行化即可。统一服务器应答的时候,也需要串行化,否则有可能会把对方给挂死。特别是在多线程情况下。
什么时候应该选择 Thrift
需要在非常多的语言间进行数据交换
对 CPU 敏感
协议层、传输层有多种控制要求
需要稳定的版本
不需要良好的文档和示例
Thrift 网络栈架构
TTransport 层
TSocket :阻塞 Socket
TFrameTransport :以 frame 为单位进行传输, 非阻塞式服务中使用
TFileTransport : 以文件形式进行传输
TProtocol 层
代表 thrift 客户端和服务端之间的传输数据的协议,指的是客户端和服务端传输数据的格式,比如 Json, thrift 中有如下格式:
TBinaryProtocol:二进制格式
TCompactProtocol:压缩格式
TJSONProtocol : Json 格式
TSimpleJsonProtocol:提供只写的 JSON 协议
Thrift 支持的 Server 模型
TSimpleServer :用于简单的单线程模型,常用于测试
TThreadPoolServer :多线程模型,使用标准的阻塞 IO
TNoBlockingServer: 多线程服务模型,使用非阻塞 IO,需要使用 TFramedTransport 数据传输方式。
THsHaServer : THsHa 引入了线程池去处理,其模型读写任务放到线程池去处理,Half-sync/Half-async 处理模式,Half-async 是在处理 IO 事件上(accept/read/write io),Half-sync 用于 handler 对 rpc 的同步处理;
Thrift 支持的数据类型以及关键字
基本数据类型
容器类型
Struct 类型
Struct:类似于 C 的 struct,是一系列相关数据的封装,在 OOP 语言中会转换为类(class),struct 的每个元素包括一个唯一的数字标识、一个数据类型、一个名称和一个可选的默认值。语法:
Union 类型
Enum 类型
Enum:Thrift 枚举类型只支持单个 32 位 int 类型数据,第一个元素如果没有给值那么默认是 0,之后的元素如果没有给值,则是在前一个元素基础上加 1,语法:
别名
Typedef:Thrift 支持 C/C++风格的类型自定义,语法:
常量
Const:定义常量,Thrift 允许使用 JSON 来定义复杂类型和 struct 类型,语法:
Exception 类型
Exception:异常跟 struct 类似,会跟目标语言本地异常集成,语法:
Service 类型
Service:service 是 Thrift 服务器提供的一系列功能列表接口,在客户端就是调用这些接口来完成操作,语法:
命名空间
定义名称空间/包名/模块等等,可以使用编程语言名称规定某一特定语言的 namespace,用*表示所有未匹配到的语言的 namespace,语法
注释
包含引用
Thrift Include:将所有声明包含的 Thrift 文档都包含到当前 Thrift 中来,语法:
C++ Include:将用户自己的 C++头文件包含到当前 Thrift 中来,语法:
安装编译器
Linux 安装编译器
Windows 安装编译器
先去官网下载编译器:https://thrift.apache.org/download
然后把编译器放在一个全局可以运行的目录下面,比如:c:/Windows。
Mac 安装编译器
编译 Thrift 文件
开始在 Kratos 微服务框架下使用 Thrift
我封装了一个 Thrift 服务,可以在 Kratos 微服务框架下直接使用:https://github.com/tx7do/kratos-transport/tree/main/transport/thrift。
实例程序的目标是从服务器获取温湿度信息,然后将温湿度信息发送给客户端。示例代码可以在单元测试里面找到。
创建 Thrift 文件
然后生成代码。生成的代码在 thrift 文件所在目录下的:gen-go/{namespace}之下。
开发服务器
首先,实现 Handler
Handler 在 Kratos 里面的使用的语义是 Service,实际应用的时候,将之实现为 Service。
实现服务端
使用WithProcessor
方法我们把之前的Handler
注册成Processor
到服务器。
开发客户端
Thrift 客户端跟 gRpc 的客户端是一样的,使用Dial
方法创建一个连接,然后直接调用 RPC 方法。
评论