1. 概述
本文将介绍如何使用 Apache Thrift 框架开发跨平台客户端-服务器应用程序。我们将涵盖以下核心内容:
- 使用 IDL 定义数据类型和服务接口
- 安装库并生成多语言源代码
- 在特定语言中实现定义的接口
- 实现客户端/服务器软件
如果只想看实战案例,可直接跳转到第 5 节。
2. Apache Thrift 简介
Apache Thrift 最初由 Facebook 开发团队创建,现由 Apache 软件基金会维护。
与专注于跨平台对象序列化/反序列化的 Protocol Buffers 相比,Thrift 的核心优势在于系统组件间的通信层。
Thrift 使用特殊的接口描述语言(IDL)定义数据类型和服务接口,这些定义存储在 *.thrift
文件中,作为编译器输入,生成不同编程语言的客户端和服务器源代码。
在项目中使用 Apache Thrift,需添加 Maven 依赖:
<dependency>
<groupId>org.apache.thrift</groupId>
<artifactId>libthrift</artifactId>
<version>0.10.0</version>
</dependency>
最新版本可在 Maven 仓库 查找。
3. 接口描述语言(IDL)
如前所述,IDL 允许以语言无关的方式定义通信接口。以下是当前支持的类型:
3.1 基本类型
-
bool
– 布尔值(true/false) -
byte
– 8 位有符号整数 -
i16
– 16 位有符号整数 -
i32
– 32 位有符号整数 -
i64
– 64 位有符号整数 -
double
– 64 位浮点数 -
string
– UTF-8 编码的文本字符串
3.2 特殊类型
-
binary
– 未编码字节序列 -
optional
– Java 8 的Optional
类型
3.3 结构体(Structs)
Thrift 结构体相当于 OOP 语言中的类,但不支持继承。结构体包含一组强类型字段,每个字段有唯一名称标识符。字段可添加注解(如数字字段 ID、可选默认值等)。
3.4 容器
Thrift 容器是强类型集合:
-
list
– 有序元素列表 -
set
– 无序唯一元素集合 -
map<type1,type2>
– 严格唯一键到值的映射
容器元素可以是任何有效 Thrift 类型。
3.5 异常
异常在功能上等同于结构体,但继承自原生异常类。
3.6 服务
服务是使用 Thrift 类型定义的通信接口,由一组命名函数组成,每个函数包含参数列表和返回类型。
4. 源代码生成
4.1 语言支持
当前支持的语言列表很长,包括:
- C++
- C#
- Go
- Haskell
- Java
- JavaScript
- Node.js
- Perl
- PHP
- Python
- Ruby
完整列表见官方文档。
4.2 使用可执行文件
下载最新版本,按需构建安装,使用以下语法:
cd path/to/thrift
thrift -r --gen [LANGUAGE] [FILENAME]
其中 [LANGUAGE]
是支持的语言,[FILENAME]
是 IDL 定义文件。
注意 -r
标志:当检测到 *.thrift
文件包含其他文件时,会递归生成代码。
4.3 使用 Maven 插件
在 pom.xml
中添加插件:
<plugin>
<groupId>org.apache.thrift.tools</groupId>
<artifactId>maven-thrift-plugin</artifactId>
<version>0.1.11</version>
<configuration>
<thriftExecutable>path/to/thrift</thriftExecutable>
</configuration>
<executions>
<execution>
<id>thrift-sources</id>
<phase>generate-sources</phase>
<goals>
<goal>compile</goal>
</goals>
</execution>
</executions>
</plugin>
执行命令:
mvn clean install
⚠️ 注意:此插件已停止维护,更多信息见项目主页。
5. 客户端-服务器应用实战
5.1 定义 Thrift 文件
创建包含异常和结构的简单服务:
namespace cpp com.baeldung.thrift.impl
namespace java com.baeldung.thrift.impl
exception InvalidOperationException {
1: i32 code,
2: string description
}
struct CrossPlatformResource {
1: i32 id,
2: string name,
3: optional string salutation
}
service CrossPlatformService {
CrossPlatformResource get(1:i32 id) throws (1:InvalidOperationException e),
void save(1:CrossPlatformResource resource) throws (1:InvalidOperationException e),
list <CrossPlatformResource> getList() throws (1:InvalidOperationException e),
bool ping() throws (1:InvalidOperationException e)
}
语法简洁直观,我们定义了命名空间(按语言划分)、异常类型、结构体和服务接口。保存为 service.thrift
文件。
5.2 编译生成代码
运行编译器生成代码:
thrift -r -out generated --gen java /path/to/service.thrift
-out
标志指定输出目录。成功后 generated
目录包含三个文件:
-
CrossPlatformResource.java
-
CrossPlatformService.java
-
InvalidOperationException.java
再生成 C++ 版本:
thrift -r -out generated --gen cpp /path/to/service.thrift
现在我们获得了同一服务接口的两种语言实现(Java 和 C++)。
5.3 实现服务接口
虽然 Thrift 已完成大部分工作,仍需实现 CrossPlatformService
。只需实现 CrossPlatformService.Iface
接口:
public class CrossPlatformServiceImpl implements CrossPlatformService.Iface {
@Override
public CrossPlatformResource get(int id)
throws InvalidOperationException, TException {
return new CrossPlatformResource();
}
@Override
public void save(CrossPlatformResource resource)
throws InvalidOperationException, TException {
saveResource();
}
@Override
public List<CrossPlatformResource> getList()
throws InvalidOperationException, TException {
return Collections.emptyList();
}
@Override
public boolean ping() throws InvalidOperationException, TException {
return true;
}
}
5.4 编写服务器
构建跨平台应用需要服务器。Thrift 自带的通信框架让实现变得简单:
public class CrossPlatformServiceServer {
public void start() throws TTransportException {
TServerTransport serverTransport = new TServerSocket(9090);
server = new TSimpleServer(new TServer.Args(serverTransport)
.processor(new CrossPlatformService.Processor<>(new CrossPlatformServiceImpl())));
System.out.print("Starting the server... ");
server.serve();
System.out.println("done.");
}
public void stop() {
if (server != null && server.isServing()) {
System.print("Stopping the server... ");
server.stop();
System.out.println("done.");
}
}
}
关键步骤:
- 定义传输层(
TServerTransport
实现),指定监听端口 - 选择服务器实现:
TSimpleServer
– 简单服务器TThreadPoolServer
– 多线程服务器TNonblockingServer
– 非阻塞多线程服务器
- 提供处理器(由 Thrift 生成的
CrossPlatformService.Processor
)
5.5 编写客户端
客户端实现同样简单:
TTransport transport = new TSocket("localhost", 9090);
transport.open();
TProtocol protocol = new TBinaryProtocol(transport);
CrossPlatformService.Client client = new CrossPlatformService.Client(protocol);
boolean result = client.ping();
transport.close();
客户端步骤:
- 定义传输层并指向服务器
- 选择协议(如
TBinaryProtocol
) - 初始化 Thrift 生成的客户端实例(
CrossPlatformService.Client
) - 直接调用
*.thrift
中定义的方法(如client.ping()
)
6. 总结
本文介绍了 Apache Thrift 的核心概念和使用步骤,并通过实战案例展示了如何构建跨平台服务。所有示例代码可在 GitHub 仓库 获取。