杰瑞科技汇

Thrift Java 调用如何实现?

  1. 定义服务:使用 Thrift IDL (Interface Definition Language) 文件来定义服务和数据结构。
  2. 生成代码:使用 Thrift 编译器将 IDL 文件编译成 Java 代码。
  3. 实现服务端:编写 Java 代码,实现 IDL 中定义的接口,并启动一个 Thrift 服务器来监听请求。
  4. 实现客户端:编写 Java 代码,创建一个客户端连接到 Thrift 服务器,并调用其提供的方法。

下面我们通过一个完整的 "Hello, World" 示例来走一遍整个流程。

Thrift Java 调用如何实现?-图1
(图片来源网络,侵删)

准备工作:安装 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)
}

解释:

Thrift Java 调用如何实现?-图2
(图片来源网络,侵删)
  • 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 目录中。

Thrift Java 调用如何实现?-图3
(图片来源网络,侵删)

添加 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.javamain 方法,一个 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();
            }
        }
    }
}

运行测试

  1. 确保服务端正在运行:运行 Server.java
  2. 运行客户端:运行 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 中的调用流程非常清晰:

  1. 定义 IDL:这是服务契约,是前后端沟通的桥梁。
  2. 生成代码:将契约转化为语言特定的代码,省去手写网络通信的麻烦。
  3. 服务端实现:实现业务逻辑,并将其暴露给 Thrift 框架。
  4. 客户端调用:通过生成的客户端代理,像调用本地方法一样调用远程服务。

选择合适的服务器模型和客户端连接方式,可以显著提升应用的性能和稳定性。

分享:
扫描分享到社交APP
上一篇
下一篇