Thrift入门:用IDL定义你的第一个RPC服务

Thrift

Thrift是一种接口描述语言和二进制通讯协议,它被用来定义和创建跨语言的服务。它被当作一个远程过程调用(RPC)框架来使用,是由Facebook为"大规模跨语言服务开发"而开发的。它通过一个代码生成引擎联合了一个软件栈,来创建不同程度的、无缝的跨平台高效服务,可以使用C#、C++(基于POSIX兼容系统)、Cappuccino、Cocoa、Delphi、Erlang、Go、Haskell、Java、Node.js、OCaml、Perl、PHP、Python、Ruby和Smalltalk。虽然它以前是由Facebook开发的,但它现在是Apache软件基金会的开源项目了。该实现被描述在2007年4月的一篇由Facebook发表的技术论文中,该论文现由Apache掌管。


IDL接口描述语言

1.基本类型

  • bool:布尔值(true 或 false)
  • byte:一个 8 位有符号整数
  • i16:一个 16 位有符号整数
  • i32:一个 32 位有符号整数
  • i64:一个 64 位有符号整数
  • double:一个 64 位浮点数
  • string:使用 UTF-8 编码编码的文本字符串
  • binary:未编码字节的序列

请注意,缺少无符号整数类型。这是因为许多编程语言中没有原生的无符号整数类型。

  1. 容器类型

Thrift 容器是强类型容器,可映射到大多数编程语言中常用和常用的容器类型。

有三种容器类型:

  • list<type>:元素为type类型的有序列表。与 java 的 List 对应。
  • set<type>:一组无序的唯一元素。与 java 的 Set 对应
  • map<type1,type2>:严格唯一的键到值的映射。与 java 的 Map 对应

容器元素可以是任何有效的 Thrift 类型。

  1. 常量类型

const 常量类型 常量名称 = 常量值 ,如

idl 复制代码
const i32 INT32CONSTANT = 9853
const map<string,string> MAPCONSTANT = {'hello':'world','goodnight':'moon'}
  1. 枚举类型

enum ,一组 32 位整数常量,不支持嵌套,如

idl 复制代码
enum Operation {
	ADD = 1,
	SUBTRACT = 2,
	MULTIPY = 3,
}

也可以省略常量值, 如

idl 复制代码
enum Operation {
	ADD,
	SUBTRACT,
	MULTIPY,
}

默认从1开始自动递增

  1. 结构体类型

struct ,封装一组不同类型的数据,与 Java 中的类对应,如

idl 复制代码
struct Work {
	1: i32 num1 = 0,
	2: i32 num2, // 默认为 optional
	3: Operation op,
	4: optional string comment,
}

字段修饰符:required(必须赋值)、optional(可选,推荐使用)。

optional 关键字表示该字段值可选,如果构建的结构体类型数据中可选字段没有设置值,则在编码生成的消息数据中不会包含可选字段

  • struct 不能继承,可以嵌套,不能嵌套自己
  • 编号不能重复成员分隔符可以是逗号(,)也可以是分号(;)
  • optional不填充就不序列化,required是必须填充,一定会序列化
  • 字段可以设置默认值
  1. 异常类型

exception,可以自定义异常中包含的数据内容,如

idl 复制代码
exception FileException {
	1: i32 code,
	2: string message
}
  1. 服务接口

service , 定义服务接口的方法和参数

使用 namespace 关键字,按语言指定包路径:

idl 复制代码
namespace <语言> <包路径>

service 服务名称 {
  返回值类型 方法名(参数列表) [throws (异常列表)],
  // 其他方法...
}

idl 复制代码
namespace java com.huang.thrift

service Calculator {
  i32 add(1:i32 a, 2:i32 b),                // 简单方法
  double divide(1:double x, 2:double y) throws (1:FileException e), // 抛出异常
  oneway void log(1:string message)         // 异步方法
}

说明:

  • 方法可以不带参数,如带参数,须指明参数的序号和参数类型
  • 方法名前须指明返回值,void 表示没有返回值
  • oneway 表示客户端发起请求后不再等待响应返回,oneway 方法必须是 void 返回类型
  • throws 表示可能抛出的异常
  1. 服务继承

使用 extends 可以继承扩展另一个服务

通过 include 引入其他文件的服务,通过 文件名.xxx 可以进行使用

idl 复制代码
include "base.thrift"
service UserService extends base.Service {
	i32 getUsername(i:i32 id) throw (1:Excepion e)
}
  1. 其他

Thrift 支持多种注释方式

idl 复制代码
// 单行注释
/* 多行注释*/

使用 typedef 可以为类型取别名,如

idl 复制代码
typedef i32 int

Thrift编译器安装

我这里只介绍在 windows 安装

下载地址:[[Apache Download Mirrors](Apache Archive Distribution Directory)](thrift.apache.org/download)

下载18版本,新版本我没解决报错问题,我不知道有没有影响,但是看着不舒服

下载完毕后,找到一个文件夹放入,并将其名称更改为thrift.exe

打开环境变量,将thrift.exe的目录写入

cmd打开黑窗口,输入thrift --version,出现版本号即安装成功

IDL文件编译

IDL 文件可以直接用来生成各种语言的代码

bash 复制代码
thrift --gen <语言> [选项] <文件名.thrift>

示例:生成Java代码

bash 复制代码
thrift --gen java user_service.thrift
// 指定输出目录
thrift --gen java -o target user_service.thrift

常用编译选项

选项 作用
-out <目录> 指定代码生成目录
-I <路径> 添加include文件搜索路径
-strict 启用严格模式(警告视为错误)
-v 显示详细编译日志
-r 递归编译include的依赖文件

Thrift 协议(Protocol)定义了数据在网络传输中的编码方式,直接影响通信效率和兼容性。以下是 Thrift 支持的主要协议类型及其核心特性

协议名称 编码方式 特点 适用场景
TBinaryProtocol 二进制编码 高性能,跨语言支持完善 默认选择,生产环境
TCompactProtocol 紧凑二进制编码 体积比二进制更小,效率更高 高吞吐、带宽敏感场景
TJSONProtocol JSON文本编码 可读性强,但体积大、效率低 调试、与前端交互
TSimpleJSONProtocol 简化JSON 仅生成JSON,无元信息 兼容非Thrift系统
TDebugProtocol 调试文本格式 人类可读,用于日志记录 开发阶段调试

编译生成代码

我在这里提前准备了一个idl的文件(.thrift 结尾)

在 thrift 的文件目录执行上面讲的编译命令,便会生成对应的java文件,从图中可以发现,有一些报错,这是因为我们没有导入thrift的相关依赖

解决报错:

导入相关依赖

xml 复制代码
<dependency>
    <groupId>org.apache.thrift</groupId>
    <artifactId>libthrift</artifactId>
    <version>0.18.0</version>
</dependency>
<dependency>
    <groupId>javax.annotation</groupId>
    <artifactId>javax.annotation-api</artifactId>
    <version>1.3.2</version>
</dependency>
<dependency>
    <groupId>ch.qos.logback</groupId>
    <artifactId>logback-classic</artifactId>
    <version>1.5.11</version>
</dependency>

入门Demo实现

服务端实现

  1. 实现定义的服务接口

把生成的代码复制到我们自己的service目录中

编写服务端逻辑,实现IDL定义的接口

然后创建一个类实现生成的这个 UserService类中的Iface接口,并实现它的抽象方法

java 复制代码
public class UserServiceImpl implements UserService.Iface {
    @Override
    public String getUserName(int userId) throws TException {
        System.out.println("getUserName, userid :" + userId);
        return "成功获取用户名!";
    }
}
  1. 启动服务端的服务器
java 复制代码
public class Service {
    public static void main(String[] args) {
        // 创建处理器和传输层
        UserService.Processor<UserServiceImpl> processor =
                new UserService.Processor<>(new UserServiceImpl());
        TServerSocket serverSocket = null;
        try {
            serverSocket = new TServerSocket(8081);
            // 配置协议和传输方式
            TServer.Args serverArgs = new TServer.Args(serverSocket)
                    .processor(processor)
                    .protocolFactory(new TBinaryProtocol.Factory());
            // 启动服务
            TServer server = new TSimpleServer(serverArgs);
            System.out.println("Server started on port 8081...");
            server.serve();
        } catch (TTransportException e) {
            throw new RuntimeException(e);
        }finally {
            if (serverSocket != null)
                serverSocket.close();
        }
    }
}

客户端实现

java 复制代码
public class Client {
    public static void main(String[] args) {
        try {
            // 配置传输和协议
            TSocket tSocket = new TSocket("localhost", 8081);
            tSocket.open();
            TProtocol protocol = new TBinaryProtocol(tSocket);
            UserService.Client client = new UserService.Client(protocol);

            // 调用远程方法
            String result = client.getUserName(156);
            System.out.println(result);
            tSocket.close();

        } catch (Exception e) {
            e.printStackTrace();
        }
    }
}

能分别从服务端和客户端看到信息的打印,就算成功了。

相关推荐
网络风云15 分钟前
Flask(三)路由与视图函数
后端·python·flask
Asthenia041227 分钟前
Java 线程的状态转换 / 操作系统线程状态转换 / 线程上下文切换详解 / 如何避免线程切换
后端
江沉晚呤时34 分钟前
深入解析外观模式(Facade Pattern)及其应用 C#
java·数据库·windows·后端·microsoft·c#·.netcore
uhakadotcom34 分钟前
云原生数据仓库对比:Snowflake、Databricks与阿里云MaxCompute
后端·面试·github
Asthenia041243 分钟前
常用索引有哪些?联合索引使用时要注意什么?什么是最左匹配原则?联合索引(a, b, c),使用(b, c) 可以命中索引吗?(a, c) 呢?
后端
Asthenia04121 小时前
Redis性能与优势/对比其他Key-Value存储/数据类型及底层结构/相同数据结构原因/对比Memcached优势/字符串最大容量/RDB与AOF分析
后端
计算机-秋大田2 小时前
基于Spring Boot的个性化商铺系统的设计与实现(LW+源码+讲解)
java·vue.js·spring boot·后端·课程设计
熬了夜的程序员2 小时前
Go 语言封装邮件发送功能
开发语言·后端·golang·log4j
uhakadotcom2 小时前
PostgreSQL 行级安全性(RLS)简介
后端·面试·github
小马爱打代码2 小时前
Spring Boot - 动态编译 Java 类并实现热加载
spring boot·后端