我们将创建一个简单的用户服务,可以创建用户和根据 ID 获取用户信息。

步骤概览
- 定义 Thrift 文件: 使用
.thrift文件定义数据结构和服务的接口。 - 生成 Java 代码: 使用 Thrift 编译器根据
.thrift文件生成 Java 代码。 - 实现服务端: 编写一个 Java 类,实现生成的
Iface接口,并启动一个 Thrift 服务器。 - 编写客户端: 编写一个 Java 客户端,连接到服务器并调用服务。
- 运行与测试: 先启动服务端,再运行客户端进行测试。
第 1 步:定义 Thrift 文件
创建一个名为 user.thrift 的文件,这个文件将定义我们的 User 数据结构以及 UserService。
user.thrift
// 指定生成的语言为 Java
namespace java com.example.thrift
// 定义一个数据结构 User
struct User {
1: required i32 id,
2: required string name,
3: optional string email,
4: optional i32 age
}
// 定义服务 UserService
service UserService {
// 创建一个用户,返回新创建的用户信息
User createUser(1: User user),
// 根据 ID 获取用户
User getUserById(1: required i32 id)
}
文件解释:
namespace java com.example.thrift: 指定生成的 Java 代码包名为com.example.thrift。struct User: 定义一个名为User的结构体,类似于 Java 的类,字段前的1:,2:是字段 ID,Thrift 内部使用,用于序列化。required表示该字段是必须的,optional表示是可选的。service UserService: 定义一个名为UserService的服务,它包含两个方法:createUser: 接收一个User对象,返回一个User对象。getUserById: 接收一个i32类型的id,返回一个User对象。
第 2 步:生成 Java 代码
你需要下载 Thrift 编译器 并配置好环境变量。

在你的项目根目录下(user.thrift 所在目录),运行以下命令:
thrift --gen java user.thrift
执行成功后,会生成一个名为 gen-java 的目录,其结构如下:
gen-java/
└── com/
└── example/
└── thrift/
├── User.java // 生成的 User 数据结构类
├── UserService.java // 生成的服务接口
└── UserService$Iface.java // 生成的服务实现接口(需要被实现)
这些就是 Thrift 帮我们生成的骨架代码。UserService$Iface 是我们需要在服务端实现的接口。
第 3 步:实现服务端
我们来编写服务端的代码,你需要一个 Java 项目,并将 gen-java 目录下的代码以及 Thrift 的 Java 库(libthrift-x.x.x.jar)添加到项目的 classpath 中。

这里以 Maven 项目为例,你的 pom.xml 应该包含以下依赖:
<dependencies>
<!-- Thrift 核心库 -->
<dependency>
<groupId>org.apache.thrift</groupId>
<artifactId>libthrift</artifactId>
<version>0.19.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.12</version>
</dependency>
</dependencies>
服务端实现代码 UserServiceImpl.java
package com.example.thrift;
import org.apache.thrift.TException;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import java.util.HashMap;
import java.util.Map;
/**
* UserService 的具体实现
*/
public class UserServiceImpl implements UserService.Iface {
private static final Logger logger = LoggerFactory.getLogger(UserServiceImpl.class);
// 使用一个简单的 Map 来模拟数据库存储
private final Map<Integer, User> userDatabase = new HashMap<>();
@Override
public User createUser(User user) throws TException {
logger.info("Creating user: {}", user);
// 为用户分配一个 ID (这里简单模拟,实际应用应从数据库获取)
int newId = userDatabase.size() + 1;
user.setId(newId);
userDatabase.put(newId, user);
return user;
}
@Override
public User getUserById(int id) throws TException {
logger.info("Getting user by id: {}", id);
User user = userDatabase.get(id);
if (user == null) {
// 如果用户不存在,可以抛出异常或返回 null
// 这里选择抛出异常,更符合 Thrift 的习惯
throw new TException("User with id " + id + " not found.");
}
return user;
}
}
服务器启动代码 Server.java
package com.example.thrift;
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;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
public class Server {
private static final Logger logger = LoggerFactory.getLogger(Server.class);
private static final int PORT = 9090;
public static void main(String[] args) {
try {
// 1. 创建传输层,监听指定端口
TServerSocket serverTransport = new TServerSocket(PORT);
// 2. 创建协议工厂,这里使用二进制协议
TBinaryProtocol.Factory protocolFactory = new TBinaryProtocol.Factory();
// 3. 创建处理器,将服务实现与接口绑定
UserService.Processor<UserServiceImpl> processor = new UserService.Processor<>(new UserServiceImpl());
// 4. 创建服务器模型,这里使用简单的单线程阻塞模型
TServer.Args args = new TServer.Args(serverTransport)
.processor(processor)
.protocolFactory(protocolFactory);
TServer server = new TSimpleServer(args);
logger.info("Starting the simple server on port {}", PORT);
// 5. 启动服务器
server.serve();
} catch (TTransportException e) {
logger.error("Server failed to start", e);
}
}
}
第 4 步:编写客户端
客户端代码相对简单,它只需要知道服务端的地址、端口,然后调用生成的 UserService.Client 即可。
客户端代码 Client.java
package com.example.thrift;
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;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
public class Client {
private static final Logger logger = LoggerFactory.getLogger(Client.class);
private static final String SERVER_HOST = "localhost";
private static final int SERVER_PORT = 9090;
public static void main(String[] args) {
// 1. 创建传输层,连接到服务器
TTransport transport = new TSocket(SERVER_HOST, SERVER_PORT);
try {
transport.open();
// 2. 创建协议层
TProtocol protocol = new TBinaryProtocol(transport);
// 3. 创建客户端
UserService.Client client = new UserService.Client(protocol);
// 4. 调用服务方法
// 4.1 创建一个用户
User newUser = new User();
newUser.setName("Alice");
newUser.setEmail("alice@example.com");
newUser.setAge(30);
User createdUser = client.createUser(newUser);
logger.info("Created user: {}", createdUser);
// 4.2 根据ID获取用户
int userId = createdUser.getId();
User fetchedUser = client.getUserById(userId);
logger.info("Fetched user: {}", fetchedUser);
// 5. 关闭传输层
transport.close();
} catch (TTransportException e) {
logger.error("Transport error", e);
} catch (TException e) {
logger.error("Thrift application error", e);
}
}
}
第 5 步:运行与测试
-
启动服务端: 运行
Server.java的main方法,你应该会在控制台看到日志:INFO c.e.t.Server - Starting the simple server on port 9090 -
运行客户端: 在服务端运行后,运行
Client.java的main方法,客户端的输出应该类似于:INFO c.e.t.Client - Created user: User(id:1, name:Alice, email:alice@example.com, age:30) INFO c.e.t.Client - Fetched user: User(id:1, name:Alice, email:alice@example.com, age:30)服务端的控制台也会打印出相应的日志:
INFO c.e.t.UserServiceImpl - Creating user: User(id:0, name:Alice, email:alice@example.com, age:30) INFO c.e.t.UserServiceImpl - Getting user by id: 1
至此,一个完整的 Thrift Java 实例就完成了,这个例子涵盖了 Thrift 开发的核心流程,你可以基于此进行扩展,例如使用更高效的 TThreadPoolServer 或 TNonblockingServer,以及更复杂的数据类型和业务逻辑。
