- 定义服务:使用 Thrift IDL (Interface Definition Language) 文件来定义服务和数据结构。
- 生成代码:使用 Thrift 编译器将 IDL 文件编译成 Java 代码。
- 实现服务端:编写 Java 代码,实现 IDL 中定义的接口,并启动一个 Thrift 服务器来监听请求。
- 实现客户端:编写 Java 代码,创建一个客户端连接到 Thrift 服务器,并调用其提供的方法。
下面我们通过一个完整的 "Hello, World" 示例来走一遍整个流程。

准备工作:安装 Thrift 编译器
你需要在你的开发环境中安装 Thrift 编译器,你可以从 Thrift 官网 下载适合你操作系统的版本,并配置好环境变量 PATH,以便在命令行中直接使用 thrift 命令。
验证安装:
thrift -version
第 1 步:定义服务 (创建 IDL 文件)
创建一个名为 hello.thrift 的文件,内容如下:
// 指定生成代码的语言
namespace java com.example.thrift.service
// 定义一个名为 Hello 的服务
service Hello {
// 定义一个名为 sayHello 的方法
// 它接收一个字符串 name,并返回一个字符串
string sayHello(1: string name)
}
解释:

namespace java com.example.thrift.service: 告诉 Thrift 编译器,将生成的 Java 代码放在com.example.thrift.service包下。service Hello: 定义了一个名为Hello的服务接口。string sayHello(1: string name): 定义了一个服务方法sayHello。string: 返回值类型。sayHello: 方法名。(1: string name): 参数列表。1是参数的序号(在 Thrift 中用于唯一标识参数),string是参数类型,name是参数名。
第 2 步:生成代码
打开命令行,进入到 hello.thrift 文件所在的目录,然后执行以下命令:
thrift --gen java hello.thrift
执行成功后,会生成一个名为 gen-java 的目录,其结构如下:
gen-java/
└── com/
└── example/
└── thrift/
└── service/
├── Hello.java // 服务接口
├── Hello$Processor.java // 服务处理器
└── HelloClient.java // 客户端存根
这些就是 Thrift 自动生成的 Java 代码,我们将这些 Java 文件(以及它们依赖的 libthrift 库)集成到我们的 Java 项目中。
第 3 步:实现服务端
我们创建一个 Java 项目(一个 Maven 项目),并将生成的 gen-java 目录下的文件复制到项目的 src/main/java 目录中。

添加 Maven 依赖
在你的 pom.xml 文件中添加 libthrift 依赖:
<dependencies>
<!-- Thrift 核心库 -->
<dependency>
<groupId>org.apache.thrift</groupId>
<artifactId>libthrift</artifactId>
<version>0.16.0</version> <!-- 使用较新的稳定版本 -->
</dependency>
<!-- 其他可选依赖,如日志 -->
<dependency>
<groupId>org.slf4j</groupId>
<artifactId>slf4j-api</artifactId>
<version>1.7.36</version>
</dependency>
<dependency>
<groupId>ch.qos.logback</groupId>
<artifactId>logback-classic</artifactId>
<version>1.2.11</version>
</dependency>
</dependencies>
编写服务端实现类
创建一个 HelloServiceImpl.java 文件,实现 com.example.thrift.service.Hello.Iface 接口。
package com.example.thrift.service;
import org.apache.thrift.TException;
// 实现 Thrift 生成的 Hello.Iface 接口
public class HelloServiceImpl implements Hello.Iface {
@Override
public String sayHello(String name) throws TException {
System.out.println("服务端收到请求: sayHello(\"" + name + "\")");
String response = "Hello, " + name + "!";
System.out.println("服务端返回响应: " + response);
return response;
}
}
启动 Thrift 服务器
创建一个 Server.java 文件来启动服务器。
package com.example.thrift.server;
import com.example.thrift.service.Hello;
import com.example.thrift.service.HelloServiceImpl;
import org.apache.thrift.TProcessor;
import org.apache.thrift.protocol.TBinaryProtocol;
import org.apache.thrift.server.TServer;
import org.apache.thrift.server.TSimpleServer;
import org.apache.thrift.transport.TServerSocket;
import org.apache.thrift.transport.TTransportException;
public class Server {
public static void main(String[] args) {
try {
// 1. 选择传输层协议(使用阻塞式 Socket)
TServerSocket serverTransport = new TServerSocket(9090);
// 2. 选择传输的数据格式(二进制协议)
TBinaryProtocol.Factory protocolFactory = new TBinaryProtocol.Factory();
// 3. 创建处理器,关联我们的服务实现
TProcessor processor = new Hello.Processor<>(new HelloServiceImpl());
// 4. 创建服务器模型(这里使用最简单的单线程模型)
TServer server = new TSimpleServer(
new TServer.Args(serverTransport)
.processor(processor)
.protocolFactory(protocolFactory)
);
System.out.println("Thrift Server 启动,监听端口 9090...");
// 5. 启动服务器
server.serve();
} catch (TTransportException e) {
e.printStackTrace();
}
}
}
你可以运行 Server.java 的 main 方法,一个 Thrift 服务器就启动了,它正在监听 9090 端口。
第 4 步:实现客户端
客户端代码同样依赖于生成的 Java 文件和 libthrift 库。
编写客户端代码
创建一个 Client.java 文件。
package com.example.thrift.client;
import com.example.thrift.service.Hello;
import org.apache.thrift.TException;
import org.apache.thrift.protocol.TBinaryProtocol;
import org.apache.thrift.protocol.TProtocol;
import org.apache.thrift.transport.TSocket;
import org.apache.thrift.transport.TTransport;
import org.apache.thrift.transport.TTransportException;
public class Client {
public static void main(String[] args) {
// 1. 创建传输层对象,指定服务器地址和端口
TTransport transport = new TSocket("localhost", 9090);
try {
// 2. 打开传输通道
transport.open();
// 3. 创建协议对象,必须与服务端一致
TProtocol protocol = new TBinaryProtocol(transport);
// 4. 创建客户端代理对象
Hello.Client client = new Hello.Client(protocol);
// 5. 调用远程方法
String result = client.sayHello("Thrift User");
System.out.println("客户端收到响应: " + result);
} catch (TException e) {
e.printStackTrace();
} finally {
// 6. 关闭传输通道
if (transport != null) {
transport.close();
}
}
}
}
运行测试
- 确保服务端正在运行:运行
Server.java。 - 运行客户端:运行
Client.java。
预期输出:
服务端控制台:
Thrift Server 启动,监听端口 9090...
服务端收到请求: sayHello("Thrift User")
服务端返回响应: Hello, Thrift User!
客户端控制台:
客户端收到响应: Hello, Thrift User!
恭喜!你已经成功地在 Java 中完成了一次 Thrift 的远程服务调用。
进阶:使用非阻塞式服务器和连接池
上面的例子使用了最简单的 TSimpleServer,它是一个单线程阻塞模型,不适合生产环境,在实际应用中,我们通常使用更高级的服务器模型,如 THsHaServer (Half-async/Half-sync) 或 TNonblockingServer (非阻塞 I/O)。
服务端改进 (使用 THsHaServer)
THsHaServer 使用一个单独的 I/O 线程来处理网络连接,并使用一个线程池来处理业务逻辑,性能更好。
// ... (Server.java 中的 main 方法)
public static void main(String[] args) {
try {
TServerSocket serverTransport = new TServerSocket(9090);
TBinaryProtocol.Factory protocolFactory = new TBinaryProtocol.Factory();
TProcessor processor = new Hello.Processor<>(new HelloServiceImpl());
// 使用 THsHaServer
TServer server = new THsHaServer(
new THsHaServer.Args(serverTransport)
.processor(processor)
.protocolFactory(protocolFactory)
// 设置工作线程池
.workerThreads(10)
);
System.out.println("THsHa Server 启动,监听端口 9090...");
server.serve();
} catch (TTransportException e) {
e.printStackTrace();
}
}
客户端改进 (使用连接池)
频繁地创建和销毁 socket 连接是非常消耗资源的,Thrift 提供了 TThreadPoolClient,它内部维护一个连接池。
// ... (Client.java 中的 main 方法)
public static void main(String[] args) {
// 使用 THttpClientTransport 也可以,但 TSocket 更常见
// TTransport transport = new TSocket("localhost", 9090);
// 使用连接池
TTransport transport = new TThreadPoolClientTransport(
new TSocket("localhost", 9090),
// 连接池配置
5, // min active
10 // max active
);
try {
transport.open();
TProtocol protocol = new TBinaryProtocol(transport);
Hello.Client client = new Hello.Client(protocol);
String result = client.sayHello("Pooled Client");
System.out.println("客户端收到响应: " + result);
} catch (TException e) {
e.printStackTrace();
} finally {
transport.close();
}
}
注意: TThreadPoolClientTransport 在较新版本的 libthrift 中可能已被弃用或功能有所变化,更常见的做法是在应用层面(如使用 Spring Cloud Alibaba 的 Dubbo 或 Sentinel)管理连接池,或者使用 TFramedTransport + TNonblockingTransport 的组合来配合非阻塞服务器,对于简单场景,手动创建和复用 TSocket 也是一个选择。
Thrift 在 Java 中的调用流程非常清晰:
- 定义 IDL:这是服务契约,是前后端沟通的桥梁。
- 生成代码:将契约转化为语言特定的代码,省去手写网络通信的麻烦。
- 服务端实现:实现业务逻辑,并将其暴露给 Thrift 框架。
- 客户端调用:通过生成的客户端代理,像调用本地方法一样调用远程服务。
选择合适的服务器模型和客户端连接方式,可以显著提升应用的性能和稳定性。
