Kratos微服务框架下实现Thrift服务

本文涉及的产品
服务治理 MSE Sentinel/OpenSergo,Agent数量 不受限
简介: Thrift是Facebook于2007年开发的跨语言的rpc服框架,提供多语言的编译功能,并提供多种服务器工作模式;用户通过Thrift的IDL(接口定义语言)来描述接口函数及数据类型,然后通过Thrift的编译环境生成各种语言类型的接口文件,用户可以根据自己的需要采用不同的语言开发客户端代码和服务器端代码。2007年由facebook贡献到apache基金,是apache下的顶级项目

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支持的数据类型以及关键字

基本数据类型

类型 说明
byte 有符号字节
i16 16 位有符号整数
i32 32 位有符号整数
i64 64 位有符号整数
double 64 位浮点数
string 字符串

容器类型

类型 说明
list 一系列由 T 类型的数据组成的有序列表, 元素可以重复
set 一系列由 T 类型组成的无序集合,元素不可以重复
map 一个字典结构,Key 为 K 类型, Value 为 V 类型,和 Java 中的 HashMap 类似thrift 支持 struct 类型,可以将一些数据类型聚合到一块。

Struct类型

Struct:类似于C的struct,是一系列相关数据的封装,在OOP语言中会转换为类(class),struct的每个元素包括一个唯一的数字标识、一个数据类型、一个名称和一个可选的默认值。语法:

struct People {
    1:string name;
    2:i32 age;
    3:string gender;
}

Union类型

union SomeUnion {
  2: string string_thing,
  3: i32 i32_thing
}

Enum类型

Enum:Thrift枚举类型只支持单个32位int类型数据,第一个元素如果没有给值那么默认是0,之后的元素如果没有给值,则是在前一个元素基础上加1,语法:

enum Gender {
    MALE,
    FEMALE
}

别名

Typedef:Thrift 支持C/C++风格的类型自定义,语法:

// typedef 原类型 自定义类型

typedef i32 int
typedef i64 long

常量

Const:定义常量,Thrift允许使用JSON来定义复杂类型和struct类型,语法:

// const 字段类型 名称标识 = 值 | 列表

const i32 MAX_RETRIES_TIME = 10;
const string MY_WEBSITE = "http://facebook.com";

Exception类型

Exception:异常跟struct类似,会跟目标语言本地异常集成,语法:

exception RequestException {
    1:i32 code;
    2:string reason;
}

Service类型

Service:service是Thrift 服务器提供的一系列功能列表接口,在客户端就是调用这些接口来完成操作,语法:

service HelloWorldService {
    // service中可以定义若干个服务,相当于Java Interface中定义的方法
    string doAction(1:string name, 2:i32 age);
}

命名空间

定义名称空间/包名/模块等等,可以使用编程语言名称规定某一特定语言的namespace,用*表示所有未匹配到的语言的namespace,语法

// namespace [语言名称] 标识符

namespace cpp api
namespace go api
namespace d api
namespace dart api
namespace java api
namespace php api
namespace perl api
namespace haxe api
namespace netstd api

// 用*表示所有未匹配到的语言的namespace
namespace * api

注释

# shell风格注释

/*
 * 多行注释
 */

// 单行注释

包含引用

Thrift Include:将所有声明包含的Thrift文档都包含到当前Thrift中来,语法:

// 包含其他的thrift文件
// inlucde "文件名"
include "other.thrift"

C++ Include:将用户自己的C++头文件包含到当前Thrift中来,语法:

// 将用户自己的C++头文件包含到当前Thrift中来
// cpp_include "头文件"
cpp_include "string"

安装编译器

Linux安装编译器

sudo apt install thrift-compiler

Windows安装编译器

先去官网下载编译器:https://thrift.apache.org/download

然后把编译器放在一个全局可以运行的目录下面,比如:c:/Windows。

Mac安装编译器

brew install thrift

编译Thrift文件

# 
thrift --gen <language> <Thrift filename>

# 如果有thrift文件中有包含其他thrift,可以使用递归生成命令
thrift -r --gen <language> <Thrift filename>

# 示例
thrift -r -gen go tutorial.thrift

开始在Kratos微服务框架下使用Thrift

我封装了一个Thrift服务,可以在Kratos微服务框架下直接使用:https://github.com/tx7do/kratos-transport/tree/main/transport/thrift

实例程序的目标是从服务器获取温湿度信息,然后将温湿度信息发送给客户端。示例代码可以在单元测试里面找到。

创建Thrift文件

namespace go api

struct Hygrothermograph {
  1: optional double Humidity,
  2: optional double Temperature,
}

service HygrothermographService {
  Hygrothermograph getHygrothermograph()
}

然后生成代码。生成的代码在thrift文件所在目录下的:gen-go/{namespace}之下。

开发服务器

首先,实现Handler

type HygrothermographHandler struct {
}

func NewHygrothermographHandler() *HygrothermographHandler {
return &HygrothermographHandler{}
}

func (p *HygrothermographHandler) GetHygrothermograph(ctx context.Context) (_r *api.Hygrothermograph, _err error) {
var Humidity = float64(rand.Intn(100))
var Temperature = float64(rand.Intn(100))
_r = &api.Hygrothermograph{
Humidity:    &Humidity,
Temperature: &Temperature,
}
fmt.Println("Humidity:", Humidity, "Temperature:", Temperature)
return
}

Handler在Kratos里面的使用的语义是Service,实际应用的时候,将之实现为Service。

实现服务端

    ctx := context.Background()

srv := NewServer(
WithAddress(":7700"),
WithProcessor(api.NewHygrothermographServiceProcessor(NewHygrothermographHandler())),
)

if err := srv.Start(ctx); err != nil {
panic(err)
}

defer func () {
if err := srv.Stop(ctx); err != nil {
t.Errorf("expected nil got %v", err)
}
}()

使用WithProcessor方法我们把之前的Handler注册成Processor到服务器。

开发客户端

conn, err := Dial(
WithEndpoint("localhost:7700"),
)
if err != nil {
t.Fatal(err)
}
defer conn.Close()

client := api.NewHygrothermographServiceClient(conn.Client)

reply, err := client.GetHygrothermograph(context.Background())
//t.Log(err)
if err != nil {
t.Errorf("failed to call: %v", err)
}
t.Log(*reply.Humidity, *reply.Temperature)

Thrift客户端跟gRpc的客户端是一样的,使用Dial方法创建一个连接,然后直接调用RPC方法。

参考文档

目录
相关文章
|
7天前
|
Java API 微服务
【Spring Boot系列】通过OpenAPI规范构建微服务服务接口
【4月更文挑战第5天】通过OpenAPI接口构建Spring Boot服务RestAPI接口
|
1月前
|
监控 持续交付 开发者
深入探讨后端服务的微服务架构设计与实践
【2月更文挑战第10天】随着互联网应用的不断发展,微服务架构作为一种灵活、高效的解决方案在后端服务开发中备受关注。本文将深入探讨微服务架构设计与实践,从服务拆分、通信机制到部署管理等方面进行详细剖析,旨在帮助开发者更好地理解和运用微服务架构。
28 2
|
1月前
|
SpringCloudAlibaba Java 网络架构
【Springcloud Alibaba微服务分布式架构 | Spring Cloud】之学习笔记(七)Spring Cloud Gateway服务网关
【Springcloud Alibaba微服务分布式架构 | Spring Cloud】之学习笔记(七)Spring Cloud Gateway服务网关
101 0
|
1天前
|
Java 数据安全/隐私保护 Sentinel
微服务学习 | Spring Cloud 中使用 Sentinel 实现服务限流
微服务学习 | Spring Cloud 中使用 Sentinel 实现服务限流
|
14天前
|
监控 Sentinel 微服务
微服务的防御之道:服务雪崩、服务熔断、服务降级
微服务的防御之道:服务雪崩、服务熔断、服务降级
20 1
|
28天前
|
Java fastjson 数据安全/隐私保护
【Dubbo3技术专题】「云原生微服务开发实战」 一同探索和分析研究RPC服务的底层原理和实现
【Dubbo3技术专题】「云原生微服务开发实战」 一同探索和分析研究RPC服务的底层原理和实现
39 0
|
1月前
|
消息中间件 SpringCloudAlibaba Java
【Springcloud Alibaba微服务分布式架构 | Spring Cloud】之学习笔记(八)Config服务配置+bus消息总线+stream消息驱动+Sleuth链路追踪
【Springcloud Alibaba微服务分布式架构 | Spring Cloud】之学习笔记(八)Config服务配置+bus消息总线+stream消息驱动+Sleuth链路追踪
785 0
|
1月前
|
SpringCloudAlibaba 负载均衡 Java
【Springcloud Alibaba微服务分布式架构 | Spring Cloud】之学习笔记(三)Eureka服务注册中心
【Springcloud Alibaba微服务分布式架构 | Spring Cloud】之学习笔记(三)Eureka服务注册中心
44 1
|
1月前
|
缓存 网络协议 微服务
微服务架构下服务注册的几种方式
【2月更文挑战第15天】微服务架构下服务注册的几种方式
35 1
|
1月前
|
存储 负载均衡 Java
【Spring底层原理高级进阶】微服务 Spring Cloud 的注册发现机制:Eureka 的架构设计、服务注册与发现的实现原理,深入掌握 Ribbon 和 Feign 的用法 ️
【Spring底层原理高级进阶】微服务 Spring Cloud 的注册发现机制:Eureka 的架构设计、服务注册与发现的实现原理,深入掌握 Ribbon 和 Feign 的用法 ️