naiguo
naiguo
发布于 2025-10-16 / 27 阅读
0
0

thrift

本文介绍下ubuntu下,基于C++从源码构建thrift,并编写典型的服务器和客户端。并基于thrift的IDL构建java库,作为客户端调用对应的服务。

系统环境

$ lsb_release -a
No LSB modules are available.
Distributor ID: Ubuntu
Description:    Ubuntu 24.04.3 LTS
Release:        24.04
Codename:       noble

安装Thrift支持C++与Java

基于参考资料,了解Thrift C++与Java模块对组件的依赖。这里特别注意,使用的gradle与java版本之间也有依赖关系。

# thrift依赖automake来构建
sudo apt-get update
sudo apt-get install libtool libtool-bin
sudo apt-get install automake bison flex g++ \
    git libboost-all-dev libevent-dev \
    libssl-dev libtool make pkg-config

# C
sudo apt install libglib2.0-dev

# Java * kotlin
# Gradle 8.14.3 可通过下载后手工安装
sudo apt install -y openjdk-11-jdk


# 下载稳定版本的源码包
wget http://archive.apache.org/dist/thrift/0.22.0/thrift-0.22.0.tar.gz
tar zxvf thrift-0.22.0.tar.gz
cd thrift-0.22.0

# 构建并安装,中间可能有错误,针对性修改
./bootstrap.sh
./configure --prefix=/home/gaoyong/soft/thrift/ --with-ruby=no --with-go=no --with-perl=no --with-php=no --with-csharp=no \
  --with-erlang=no --with-lua=no --with-nodejs=no --without-python 

make CPPFLAGS=-DFORCE_BOOST_SMART_PTR -j 4 -s
make install

本文支持了四个语言,可在执行configure之后看到

C (glib):
   Using glib version ........ : 2.80.0

C++ Library:
   C++ compiler .............. : g++ -std=c++11
   Build TZlibTransport ...... : yes
   Build TNonblockingServer .. : yes
   Build TQTcpServer (Qt5) ... : no
   C++ compiler version ...... : g++ (Ubuntu 13.3.0-6ubuntu2~24.04) 13.3.0

Java Library:
   Using gradle .............. : /home/gaoyong/.gradle/wrapper/dists/gradle-8.14.3-bin/cv11ve7ro1n3o1j4so8xd9n66/gradle-8.14.3/bin/gradle
   Using java ................ : java
   Using javac ............... : javac
   Using Gradle version ...... : Gradle 8.14.3
   Using java version ........ : openjdk version "11.0.28" 2025-07-15

Kotlin (Test Only) Library:
   Using gradle .............. : /home/gaoyong/.gradle/wrapper/dists/gradle-8.14.3-bin/cv11ve7ro1n3o1j4so8xd9n66/gradle-8.14.3/bin/gradle
   Using java ................ : java
   Using javac ............... : javac
   Using Gradle version ...... : Gradle 8.14.3
   Using java version ........ : openjdk version "11.0.28" 2025-07-15

为了便于cmake动态找到thrift地址,确认/home/gaoyong/soft/thrift/lib/pkgconfig已经有了相应的文件

# 如果没有对应的thrift配置文件,创建
sudo mkdir -p /home/gaoyong/soft/thrift/lib/pkgconfig
sudo tee /home/gaoyong/soft/thrift/lib/pkgconfiglibthrift.pc <<EOF
prefix=/home/gaoyong/soft/thrift/
exec_prefix=${prefix}
libdir=${exec_prefix}/lib
includedir=${prefix}/include

Name: Thrift
Description: Thrift C++ Library
Version: 0.22.0
Libs: -L${libdir} -lthrift
Cflags: -I${includedir}
EOF

# 如果有,设置路径
echo 'export PKG_CONFIG_PATH=/home/gaoyong/soft/thrift/lib/pkgconfig:$PKG_CONFIG_PATH' >> ~/.bashrc
source ~/.bashrc

# 验证这些指令是否正常,cmake依赖其定位thrift路径
pkg-config --modversion thrift

# 检查编译和链接参数
echo "头文件参数: $(pkg-config --cflags thrift)"
echo "库链接参数: $(pkg-config --libs thrift)"

构建成功之后,可通过make install安装,默认不会安装java的jar包到目标目录。另外python基于其兼容旧版本的考虑,默认放到了固定目录,prefix无效(这里我禁用了python,实际比较友好)。将java包手工拷贝之后,可在目标安装目录看到如下结构:

:~/soft/thrift$ tree
.
├── bin
│   └── thrift
├── include
│   └── thrift
│       ├── async
│       │   ├── TAsyncBufferProcessor.h
│       │   ├── TAsyncChannel.h
│       │   ├── TAsyncDispatchProcessor.h
│       │   ├── TAsyncProcessor.h
│       │   ├── TAsyncProtocolProcessor.h
│       │   ├── TConcurrentClientSyncInfo.h
│       │   ├── TEvhttpClientChannel.h
│       │   └── TEvhttpServer.h
│       ├── c_glib
│       │   ├── config.h
│       │   ├── processor
│       │   │   ├── thrift_dispatch_processor.h
│       │   │   ├── thrift_multiplexed_processor.h
│       │   │   └── thrift_processor.h
│       │   ├── protocol
│       │   │   ├── thrift_binary_protocol_factory.h
│       │   │   ├── thrift_binary_protocol.h
│       │   │   ├── thrift_compact_protocol_factory.h
│       │   │   ├── thrift_compact_protocol.h
│       │   │   ├── thrift_multiplexed_protocol.h
│       │   │   ├── thrift_protocol_decorator.h
│       │   │   ├── thrift_protocol_factory.h
│       │   │   ├── thrift_protocol.h
│       │   │   └── thrift_stored_message_protocol.h
│       │   ├── server
│       │   │   ├── thrift_server.h
│       │   │   └── thrift_simple_server.h
│       │   ├── thrift_application_exception.h
│       │   ├── thrift_configuration.h
│       │   ├── thrift.h
│       │   ├── thrift_struct.h
│       │   └── transport
│       │       ├── thrift_buffered_transport_factory.h
│       │       ├── thrift_buffered_transport.h
│       │       ├── thrift_fd_transport.h
│       │       ├── thrift_framed_transport_factory.h
│       │       ├── thrift_framed_transport.h
│       │       ├── thrift_memory_buffer.h
│       │       ├── thrift_platform_socket.h
│       │       ├── thrift_server_socket.h
│       │       ├── thrift_server_transport.h
│       │       ├── thrift_socket.h
│       │       ├── thrift_ssl_socket.h
│       │       ├── thrift_transport_factory.h
│       │       ├── thrift_transport.h
│       │       ├── thrift_zlib_transport_factory.h
│       │       └── thrift_zlib_transport.h
│       ├── concurrency
│       │   ├── Exception.h
│       │   ├── FunctionRunner.h
│       │   ├── Monitor.h
│       │   ├── Mutex.h
│       │   ├── ThreadFactory.h
│       │   ├── Thread.h
│       │   ├── ThreadManager.h
│       │   └── TimerManager.h
│       ├── config.h
│       ├── processor
│       │   ├── PeekProcessor.h
│       │   ├── StatsProcessor.h
│       │   └── TMultiplexedProcessor.h
│       ├── protocol
│       │   ├── TBase64Utils.h
│       │   ├── TBinaryProtocol.h
│       │   ├── TBinaryProtocol.tcc
│       │   ├── TCompactProtocol.h
│       │   ├── TCompactProtocol.tcc
│       │   ├── TDebugProtocol.h
│       │   ├── TEnum.h
│       │   ├── THeaderProtocol.h
│       │   ├── TJSONProtocol.h
│       │   ├── TList.h
│       │   ├── TMap.h
│       │   ├── TMultiplexedProtocol.h
│       │   ├── TProtocolDecorator.h
│       │   ├── TProtocolException.h
│       │   ├── TProtocol.h
│       │   ├── TProtocolTap.h
│       │   ├── TProtocolTypes.h
│       │   ├── TSet.h
│       │   └── TVirtualProtocol.h
│       ├── qt
│       │   ├── TQIODeviceTransport.h
│       │   └── TQTcpServer.h
│       ├── server
│       │   ├── TConnectedClient.h
│       │   ├── TNonblockingServer.h
│       │   ├── TServerFramework.h
│       │   ├── TServer.h
│       │   ├── TSimpleServer.h
│       │   ├── TThreadedServer.h
│       │   └── TThreadPoolServer.h
│       ├── TApplicationException.h
│       ├── TBase.h
│       ├── TConfiguration.h
│       ├── TDispatchProcessor.h
│       ├── thrift-config.h
│       ├── thrift_export.h
│       ├── Thrift.h
│       ├── TLogging.h
│       ├── TNonCopyable.h
│       ├── TOutput.h
│       ├── TProcessor.h
│       ├── transport
│       │   ├── PlatformSocket.h
│       │   ├── SocketCommon.h
│       │   ├── TBufferTransports.h
│       │   ├── TFDTransport.h
│       │   ├── TFileTransport.h
│       │   ├── THeaderTransport.h
│       │   ├── THttpClient.h
│       │   ├── THttpServer.h
│       │   ├── THttpTransport.h
│       │   ├── TNonblockingServerSocket.h
│       │   ├── TNonblockingServerTransport.h
│       │   ├── TNonblockingSSLServerSocket.h
│       │   ├── TPipe.h
│       │   ├── TPipeServer.h
│       │   ├── TServerSocket.h
│       │   ├── TServerTransport.h
│       │   ├── TShortReadTransport.h
│       │   ├── TSimpleFileTransport.h
│       │   ├── TSocket.h
│       │   ├── TSocketPool.h
│       │   ├── TSocketUtils.h
│       │   ├── TSSLServerSocket.h
│       │   ├── TSSLSocket.h
│       │   ├── TTransportException.h
│       │   ├── TTransport.h
│       │   ├── TTransportUtils.h
│       │   ├── TVirtualTransport.h
│       │   ├── TWebSocketServer.h
│       │   └── TZlibTransport.h
│       ├── TToString.h
│       └── TUuid.h
└── lib
    ├── libthrift-0.22.0.jar
    ├── libthrift-0.22.0-javadoc.jar
    ├── libthrift-0.22.0-SNAPSHOT.jar
    ├── libthrift-0.22.0.so
    ├── libthrift-0.22.0-sources.jar
    ├── libthrift.a
    ├── libthrift_c_glib.a
    ├── libthrift_c_glib.la
    ├── libthrift_c_glib.so -> libthrift_c_glib.so.0.0.0
    ├── libthrift_c_glib.so.0 -> libthrift_c_glib.so.0.0.0
    ├── libthrift_c_glib.so.0.0.0
    ├── libthrift.la
    ├── libthriftnb-0.22.0.so
    ├── libthriftnb.a
    ├── libthriftnb.la
    ├── libthriftnb.so -> libthriftnb-0.22.0.so
    ├── libthrift.so -> libthrift-0.22.0.so
    ├── libthriftz-0.22.0.so
    ├── libthriftz.a
    ├── libthriftz.la
    ├── libthriftz.so -> libthriftz-0.22.0.so
    └── pkgconfig
        ├── libthrift.pc
        ├── thrift_c_glib.pc
        ├── thrift-nb.pc
        ├── thrift.pc
        └── thrift-z.pc

基于C++开发服务与客户端

编写IDL

下面是一个名字为echo.thrift的接口定义文件


namespace cpp echo
namespace java com.example.echo

service EchoService {
    string echo(1:string message)
    i32 add(2:i32 a, 3:i32 b)
}

用IDL生成C++代码

$ thrift gen-cpp -o ./target_dir/echo.thrift

$ tree ./target_dir
.
├── EchoService.cpp
├── EchoService.h
├── EchoService_server.skeleton.cpp
└── echo_types.h

实现服务端

服务端重点参考EchoService.h文件中的关于接口的虚基类,如下

...
class EchoServiceIf {
 public:
  virtual ~EchoServiceIf() {}
  virtual void echo(std::string& _return, const std::string& message) = 0;
  virtual int32_t add(const int32_t a, const int32_t b) = 0;
};
...

完整服务端的代码如下:

// cat src/cpp/EchoServer.cpp
#include <thrift/transport/TSocket.h>
#include <thrift/transport/TBufferTransports.h>
#include <thrift/protocol/TBinaryProtocol.h>
#include <thrift/server/TSimpleServer.h>
#include <thrift/transport/TServerSocket.h>
#include <thrift/transport/TTransportUtils.h>
#include <iostream>
#include <stdexcept>
#include <sstream>

// 包含生成的代码
#include "gen-cpp/EchoService.h"

using namespace std;
using namespace apache::thrift;
using namespace apache::thrift::protocol;
using namespace apache::thrift::transport;
using namespace apache::thrift::server;

using namespace echo;

// 实现EchoService接口
class EchoServiceHandler : virtual public EchoServiceIf {
public:
    EchoServiceHandler() {
        // 初始化代码(如果需要)
    }

    // echo方法:通过引用参数返回字符串(Thrift生成的签名要求)
    void echo(std::string& _return, const string& message) {
        cout << "收到echo请求: " << message << endl;
        _return = message; // 通过引用返回结果
    }

    // add方法:直接返回int32_t(根据当前生成的代码签名调整)
    int32_t add(const int32_t a, const int32_t b) {
        cout << "收到add请求: " << a << " + " << b << endl;
        return a + b; // 直接返回计算结果
    }
};

int main(int argc, char **argv) {
    int port = 9090; // 服务端口

    // 创建处理器
    shared_ptr<EchoServiceHandler> handler(new EchoServiceHandler());
    shared_ptr<TProcessor> processor(new EchoServiceProcessor(handler));

    // 设置服务端口
    shared_ptr<TServerTransport> serverTransport(new TServerSocket(port));
    shared_ptr<TTransportFactory> transportFactory(new TBufferedTransportFactory());
    shared_ptr<TProtocolFactory> protocolFactory(new TBinaryProtocolFactory());

    // 创建并启动服务器
    TSimpleServer server(processor, serverTransport, transportFactory, protocolFactory);
    cout << "Echo服务已启动,监听端口 " << port << endl;
    server.serve();

    return 0;
}

实现客户端

客户端关注EchoService.h的方法,不过关注的是如何使用,另外注意使用的是文件中定义的EchoServiceClient类。完整的客户端代码如下:

// cat src/cpp/EchoClient.cpp
#include <thrift/transport/TSocket.h>
#include <thrift/transport/TBufferTransports.h>
#include <thrift/protocol/TBinaryProtocol.h>
#include <iostream>

// 包含生成的代码
#include "gen-cpp/EchoService.h"

using namespace std;
using namespace apache::thrift;
using namespace apache::thrift::protocol;
using namespace apache::thrift::transport;

using namespace echo;

int main(int argc, char **argv) {
    string host = "localhost";
    int port = 9090;

    // 创建传输和协议
    shared_ptr<TTransport> socket(new TSocket(host, port));
    shared_ptr<TTransport> transport(new TBufferedTransport(socket));
    shared_ptr<TProtocol> protocol(new TBinaryProtocol(transport));

    // 创建客户端
    EchoServiceClient client(protocol);

    try {
        // 连接到服务器
        transport->open();

        // 调用echo方法
        string echo_response;
        client.echo(echo_response,"Hello from C++ client!");
        cout << "echo响应: " << echo_response << endl;

        // 调用add方法
        int32_t add_response = client.add(10, 20);
        cout << "add响应: 10 + 20 = " << add_response << endl;

        // 关闭连接
        transport->close();
    } catch (TException& tx) {
        cerr << "Thrift异常: " << tx.what() << endl;
    }

    return 0;
}

使用cmake构建

这里示范一个简单的工程,相应目录结构如下:

├── CMakeLists.txt
├── readme.md
└── src
    ├── cpp
    │   ├── EchoClient.cpp
    │   └── EchoServer.cpp
    └── idl
         └── echo.thrift

在这样的文件结果下,CMakeLists.txt的内容如下:

cmake_minimum_required(VERSION 3.10)
project(EchoProject)

# 设置构建类型
if(NOT CMAKE_BUILD_TYPE)
    set(CMAKE_BUILD_TYPE Release)
endif()

# 查找Thrift编译器
find_program(THRIFT_COMPILER thrift)
if(NOT THRIFT_COMPILER)
    message(FATAL_ERROR "Thrift compiler not found! Please install thrift.")
endif()

# 查找Thrift C++库
find_package(PkgConfig REQUIRED)
pkg_check_modules(THRIFT REQUIRED thrift)

# 添加 Thrift 库的搜索路径(关键步骤)
link_directories(${THRIFT_LIBRARY_DIRS})

# 查找Java
find_package(Java COMPONENTS Runtime REQUIRED)

# 设置Thrift文件路径
set(THRIFT_FILE ${CMAKE_CURRENT_SOURCE_DIR}/src/idl/echo.thrift)

# 设置生成代码的目录
set(CPP_GEN_DIR ${CMAKE_BINARY_DIR}/gen-cpp)

# 确保生成目录存在
file(MAKE_DIRECTORY ${CPP_GEN_DIR})

# 定义Thrift生成的文件(基于命名约定)
set(THRIFT_GEN_CPP_FILES
    ${CPP_GEN_DIR}/EchoService.cpp
)

# 阶段1: 生成C++ Thrift代码
add_custom_command(
    OUTPUT ${THRIFT_GEN_CPP_FILES}
    COMMAND ${THRIFT_COMPILER} --gen cpp -o ${CMAKE_BINARY_DIR} ${THRIFT_FILE}
    DEPENDS ${THRIFT_FILE}
    COMMENT "阶段1: 生成C++ Thrift代码"
    WORKING_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR}
)

# 创建自定义目标来触发代码生成
add_custom_target(generate_thrift_cpp DEPENDS ${THRIFT_GEN_CPP_FILES})

# 阶段2: 构建EchoServer(包含生成的Thrift文件)
add_executable(EchoServer
    src/cpp/EchoServer.cpp
    ${THRIFT_GEN_CPP_FILES}  # 直接包含生成的源文件
)

target_include_directories(EchoServer PRIVATE
    ${CMAKE_BINARY_DIR}
    ${CPP_GEN_DIR}
    ${THRIFT_INCLUDE_DIRS}
)

target_link_libraries(EchoServer ${THRIFT_LIBRARIES} pthread)
# 确保Thrift代码先生成
add_dependencies(EchoServer generate_thrift_cpp)

# 阶段3: 构建EchoClient(包含生成的Thrift文件)
add_executable(EchoClient
    src/cpp/EchoClient.cpp
    ${THRIFT_GEN_CPP_FILES}  # 直接包含生成的源文件
)

target_include_directories(EchoClient PRIVATE
    ${CMAKE_BINARY_DIR}
    ${CPP_GEN_DIR}
    ${THRIFT_INCLUDE_DIRS}
)

target_link_libraries(EchoClient ${THRIFT_LIBRARIES} pthread)
# 确保Thrift代码先生成
add_dependencies(EchoClient generate_thrift_cpp)

# 为每个阶段创建别名,方便单独调用
add_custom_target(gen_cpp DEPENDS generate_thrift_cpp)
add_custom_target(build_server DEPENDS EchoServer)
add_custom_target(build_client DEPENDS EchoClient)

# 构建所有目标
add_custom_target(cpp_all
    DEPENDS EchoServer EchoClient
    COMMENT "构建所有目标"
)


# 默认目标
add_custom_target(default ALL DEPENDS cpp_all)

# 显示构建信息
message(STATUS "构建类型: ${CMAKE_BUILD_TYPE}")
message(STATUS "Thrift编译器: ${THRIFT_COMPILER}")
message(STATUS "Thrift包含目录: ${THRIFT_INCLUDE_DIRS}")
message(STATUS "Thrift库: ${THRIFT_LIBRARIES}")

执行构建的步骤如下:

# 生成Ninja构建
cmake -S . -B build -G Ninja
# 执行构建 :如下两个指令等价
cmake --build build/
pushd build ; ninja; popd
# 清空构建
cmake --build build/ --target clean
pushd build ; ninja clean; popd

测试

./build/EchoServer &
./build/EchoClient
# 测试完毕,干掉Server
 killall EchoServer

$ time build/EchoClient 127.0.0.1
echo响应: Hello from C++ client!
add响应: 10 + 20 = 30

real    0m0.004s
user    0m0.002s
sys     0m0.002s

$ time ./build/EchoClient 172.21.168.128 9090
echo响应: Hello from C++ client!
add响应: 10 + 20 = 30

real    0m0.005s
user    0m0.002s
sys     0m0.001s

基于Java开发客户端

原则上Java想调用Thrift编写的服务,只需要获得IDL文件即可。其他可以通过同样版本的Thrift获得。

JDK

需要准备准备好JDK11,以及gradle环境。如果装了多个java需要切换可参考如下维度选择:

# 系统级别 : 但是优先级会被后面的覆盖
sudo update-alternatives --config java
# 环境变量 : 比较灵活,影响用户会话阶段,通过.bashrc实现持久
export JAVA_HOME= 
export PATH=$JAVA_HOME/bin:$PATH
# 工程设定 : 例如gradle
org.gradle.java.home=/usr/lib/jvm/java-17-openjdk-amd64
# SDKMAN辅助
类似pyenv之类的用来管理jdk版本的,适合开发者有多重版本不断切换的诉求

sudo apt install openjdk-11-jdk

gradle

可以从官网获得gradle对应的安装包,然后自行解压到特定目录,一般建议为~/.gradle/wrapper/dists/gradle-8.14.3-bin/cv11ve7ro1n3o1j4so8xd9n66/gradle-8.14.3/bin/,然后配置到PATH能量。

# 可以直接使用gradle build 开始构建,一般都会通过如下命令生成一个gradlew
gradle wrapper
./gradlew build

实现客户端

可以通过命令从IDL获得java代码,代码很长,但是客户端引用后,只要按照规则使用这块代码。

$thrift --gen java -o ./j/ src/main/thrift/echo.thrift
$tree j
j
└── gen-java
    └── com
        └── example
            └── echo
                └── EchoService.java

客户端的完整代码如下:

package com.example.echo;

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;

// 导入生成的Java代码
import com.example.echo.EchoService;

public class EchoClient {
    public static void main(String[] args) {
        // 默认连接参数
        String host = args.length > 0 ? args[0] : "localhost";
        int port = args.length > 1 ? Integer.parseInt(args[1]) : 9090;

        TTransport transport = null;
        try {
            // 创建传输
            transport = new TSocket(host, port);

            // 打开连接
            transport.open();

            // 创建协议
            TProtocol protocol = new TBinaryProtocol(transport);

            // 创建客户端
            EchoService.Client client = new EchoService.Client(protocol);

            // 调用echo方法
            String echoResponse = client.echo("Hello from Java client!");
            System.out.println("echo响应: " + echoResponse);

            // 调用add方法
            int addResponse = client.add(30, 40);
            System.out.println("add响应: 30 + 40 = " + addResponse);

        } catch (TTransportException e) {
            System.err.println("传输层错误: " + e.getMessage());
            e.printStackTrace();
        } catch (TException e) {
            System.err.println("Thrift调用错误: " + e.getMessage());
            e.printStackTrace();
        } catch (Exception e) {
            System.err.println("未知错误: " + e.getMessage());
            e.printStackTrace();
        } finally {
            // 确保连接被关闭
            if (transport != null) {
                transport.close();
            }
        }
    }
}

只需要特别关注下通过EchoService.Client client = new EchoService.Client(protocol);获得对象, 其他与在IDL中一样。

工程目录

这里演示一个极简单的工程目录,演示通过gradle构建Java客户端。

tree
.
├── build.gradle
├── lib
├── settings.gradle
└── src
    └── main
        ├── java
        │   └── com
        │       └── example
        │           └── echo
        │               └── EchoClient.java
        └── thrift
            └── echo.thrift

上述目录下的build.gradle内容如下:

plugins {
    id 'java'
}

// 严格离线模式:仅使用本地lib/目录
repositories {
 // 优先查找本地 lib/ 目录
    flatDir {
        dirs 'lib'
        content {
            // 仅在本地找不到时,才允许从远程下载
            excludeGroupByRegex '.*'  // 强制优先使用本地文件
        }
    }
    // 本地找不到时,从远程仓库下载
    mavenCentral()
}

dependencies {
// 本地依赖(如果存在)
    implementation fileTree(dir: 'lib', include: '*.jar')

    // 远程依赖(本地不存在时下载)
    implementation 'com.google.code.findbugs:jsr305:3.0.2'
    implementation 'org.slf4j:slf4j-api:1.7.36'
    implementation 'ch.qos.logback:logback-classic:1.2.11'
    implementation 'org.apache.thrift:libthrift:0.22.0'
    implementation 'com.google.code.findbugs:jsr305:3.0.2'  // 提供 javax.annotation
    implementation 'javax.annotation:javax.annotation-api:1.3.2'
}

// 生成代码时禁用注解(关键!)
task generateThrift(type: Exec) {
    commandLine '/home/gaoyong/soft/thrift/bin/thrift',
            '--gen', 'java',
            '-o', "${projectDir}/build/generated-sources",
            "${projectDir}/src/main/thrift/echo.thrift"
    doFirst { mkdir 'build/generated-sources' }
}

// 将生成的代码加入编译
sourceSets.main.java.srcDirs += 'build/generated-sources/gen-java'
compileJava.dependsOn generateThrift

// 打包可执行JAR(包含所有依赖)
jar {
    manifest {
        attributes(
            'Main-Class': 'com.example.echo.EchoClient',
            'Class-Path': configurations.runtimeClasspath.files.collect { it.name }.join(' ')
        )
    }
    from {
        configurations.runtimeClasspath.collect {
            it.isDirectory() ? it : zipTree(it)
        }
    }
    duplicatesStrategy = DuplicatesStrategy.EXCLUDE
    archiveFileName = "echo-client.jar"
}

// 添加运行任务(可选)
task runClient(type: JavaExec) {
    classpath = sourceSets.main.runtimeClasspath
    mainClass = 'com.example.echo.EchoClient'
    args = ['localhost', '9090']  // 默认参数
}

构建

# 基于全局的gradle直接构建和清除
gradle build
gradle clean
# 按照常规生成gradlew的方式
gradle wrapper
./gradlew build
./gradlew clean

测试

$ java -jar build/libs/echo-client.jar 
echo响应: Hello from Java client!
add响应: 30 + 40 = 70

$ time java -jar build/libs/echo-client.jar
echo响应: Hello from Java client!
add响应: 30 + 40 = 70

real    0m0.119s
user    0m0.171s
sys     0m0.034s

$ time java -jar build/libs/echo-client.jar 172.21.168.128
echo响应: Hello from Java client!
add响应: 30 + 40 = 70

real    0m0.210s
user    0m0.162s
sys     0m0.019s

异常

对python,指定的prefix无效,这里看起来应该结合python的运行机制来调整,这里通过屏蔽python先搁置

# We're ignoring prefix here because site-packages seems to be
# the equivalent of /usr/local/lib in Python land.
# Old version (can't put inline because it's not portable).
#$(PYTHON) setup.py install --prefix=$(prefix) --root=$(DESTDIR) $(PYTHON_SETUPUTIL_ARGS)
install-exec-hook:
        $(PYTHON) -m pip install . --root=$(DESTDIR) --prefix=$(PY_PREFIX) $(PYTHON_SETUPUTIL_ARGS)

参考资料

  1. 可选包的前置条件 https://thrift.apache.org/docs/install/debian

  2. Thrift的IDL https://thrift.apache.org/docs/idl.html

  3. 可下载的成熟版本 http://archive.apache.org/dist/thrift/

  4. Gradle https://gradle.org/


评论