目录

一、 背景与痛点

二、 原因剖析

三、 踩坑历程与避坑指南

坑 1:GCC 编译报错:无法识别的命令行选项 "-m64"

坑 2:编译 Boost 1.58.0 产生架构格式错误

坑 3:链接 librocketmq.so 时报 OpenSSL 重定位冲突(relocation R_AARCH64_PREL64)

四、 保姆级离线编译与部署流程

步骤 1:本地源装齐编译工具链

步骤 2:下载并准备离线依赖包

步骤 3:修改配置与脚本

步骤 4:清理缓存并执行编译

步骤 5:部署与 Python 导入验证

五、 补充建议


一、 背景与痛点

在国产化(信创)大背景下,越来越多的企业级应用开始向 ARM64 (aarch64) 架构(如飞腾、鲲鹏 CPU)以及国产操作系统(如 麒麟 Kylin Linux Advanced Server V10)迁移。

在一次项目对接中,由于历史业务设计,需要对接 RocketMQ 4.9.2 服务端。开发团队选择了官方推荐的 RocketMQ Python 客户端 rocketmq-client-python。然而,在生产环境部署时遇到了两大痛点:

  1. 环境离线:生产环境服务器 A 无法上外网,无法直接通过 pip 或联网编译工具下载依赖。
  1. 导入报错:离线安装了 rocketmq-client-python 之后,在 Python 中执行 import rocketmq.client 抛出以下错误:
Traceback (most recent call last):
  File "<string>", line 1, in <module>
  File "/opt/py/lib/python3.8/site-packages/rocketmq/client.py", line 24, in <module>
    from .ffi import (
  File "/opt/py/lib/python3.8/site-packages/rocketmq/ffi.py", line 40, in <module>
    raise ImportError('rocketmq dynamic library not found')
ImportError: rocketmq dynamic library not found

二、 原因剖析

rocketmq-client-python 的底层并非纯 Python 实现,它本质上是通过 Python 的 ctypes 模块对 RocketMQ 官方的 C++ 客户端(rocketmq-client-cpp)进行了一层封装。因此,Python 运行的前提是系统中必须存在且能成功加载编译好的 C++ 动态链接库 librocketmq.so

由于官方发布的 Wheel 包在 ARM64 架构下未正确打入适配的 librocketmq.so 动态库,我们需要在同架构的 ARM64 机器上通过 源码编译 rocketmq-client-cpp-2.2.0 来生成 librocketmq.so

而在 aarch64 / 国产麒麟系统下,官方默认的 build.sh 脚本和依赖库存在大量 x86 硬编码与架构兼容问题。以下是本次编译过程中踩过的深坑及解决方案。


三、 踩坑历程与避坑指南

坑 1:GCC 编译报错:无法识别的命令行选项 "-m64"

  • 现象:执行 build.sh 初始配置时,CMake 阶段即报错,提示无法识别 -m64
  • 原因-m32-m64 是 x86 架构(Intel/AMD 芯片)专属的编译器选项。ARM64 (aarch64) 平台的 GCC 默认且只能编译 64 位代码,不需要此参数。如果硬传,GCC 反而会报错拒绝编译。
  • 解决办法
    rocketmq-client-cpp 的根目录下,打开 CMakeLists.txt,将包含 -m32-m64 的代码行全部注释掉:
# 187行修改为:
# list(APPEND CXX_FLAGS "-m32")

# 189行修改为:
# list(APPEND CXX_FLAGS "-m64")

坑 2:编译 Boost 1.58.0 产生架构格式错误

  • 现象:编译生成的 Boost 库在后续链接时失效,或者编译报错。
  • 原因:默认 build.sh 脚本在调用 Boost 的构建工具 ./b2 时,没有指定 ARM 平台的编译选项,导致其无法自动识别 target 为 64 位的 ARM 架构。
  • 解决办法
    打开 build.sh,定位到 BuildBoost 函数(大约在第 350-380 行),在 ./b2 的执行参数中,显式指定架构和地址模型:
# 修改前:
# ./b2 -j$cpu_num cflags=-fPIC cxxflags=-fPIC --with-atomic ...

# 修改后(追加 architecture=arm address-model=64):
./b2 -j$cpu_num cflags=-fPIC cxxflags=-fPIC architecture=arm address-model=64 --with-atomic --with-thread --with-system --with-chrono --with-date_time --with-log --with-regex --with-serialization --with-filesystem --with-locale --with-iostreams threading=multi link=static release install --prefix=${install_lib_dir}

坑 3:链接 librocketmq.so 时报 OpenSSL 重定位冲突(relocation R_AARCH64_PREL64)

  • 现象:在最后的动态库链接阶段报错:
/usr/bin/ld: ../../bin/lib/libcrypto.a(sha1-armv8.o): relocation R_AARCH64_PREL64 against symbol `OPENSSL_armcap_P' which may bind externally can not be used when making a shared object; recompile with -fPIC
  • 原因:OpenSSL (1.1.1d) 内部的汇编代码(如 sha1-armv8.Schacha-armv8.S 等)在 ARM64 架构下,对全局变量 OPENSSL_armcap_P 的重定位方式与共享库的要求冲突。即使传入 CFLAGS=-fPIC,这些底层汇编模块在静态编译成 libcrypto.a 并链接入 librocketmq.so 时,依然通不过 PIC 校验。
  • 解决办法
    在编译 OpenSSL 时,通过传入 no-asm 配置标志禁用全部汇编优化,使其完全使用纯 C 语言编译。纯 C 代码在 -fPIC 作用下能 100% 保证生成合法的位置无关重定位信息。
    同时,避免在配置选项中直接写 CFLAGS=xxx 参数导致 OpenSSL 配置参数混淆报错,应将编译参数写在命令最前端:
# 将 build.sh 中编译 OpenSSL 的行修改为(以环境变量前缀传递 CFLAGS):
CFLAGS="-fPIC" CPPFLAGS="-fPIC" ./config no-asm -fPIC shared --prefix=${install_lib_dir} --openssldir=${install_lib_dir}

四、 保姆级离线编译与部署流程

基于上述踩坑经验,以下是在不能上外网的服务器 A(Kylin V10 aarch64)上离线编译部署的完整步骤:

步骤 1:本地源装齐编译工具链

虽然服务器无法上外网,但通常麒麟系统配置了内网/局域网 Yum 源。在 A 上执行:

yum install -y gcc gcc-c++ cmake make autoconf automake libtool bzip2-devel zlib-devel unzip

(若提示某些包不存在,可在能上网的机器 B 上通过 yumdownloader --resolve <包名> 下载 rpm 后拷到 A 上离线安装。)

步骤 2:下载并准备离线依赖包

在能上网的机器上下载 rocketmq-client-cpp-2.2.0 的 5 个核心依赖包,并上传至服务器 A 源码目录下的 tmp_down_dir(若没有则新建该目录)。以下是这 5 个包的下载地址:

  1. openssl-1.1.1d.tar.gz
    • 下载地址:https://mirrors.tencent.com/openssl/source/old/1.1.1/openssl-1.1.1d.tar.gz
  1. libevent-release-2.1.11-stable.zip
    • 下载地址:https://github.com/libevent/libevent/archive/refs/tags/release-2.1.11-stable.zip

[!WARNING]
如果在编译时,build.sh 脚本报错找不到 libevent,可能是因为解压后的目录名对不上。此时只需将 libevent-release-2.1.11-stable.zip 解压,将解压后的目录名重命名为 libevent-2.1.11-stable,然后重新压缩成 libevent-2.1.11-stable.zip 并放入 tmp_down_dir 即可。

  1. jsoncpp-0.10.7.zip
    • 下载地址:https://github.com/open-source-parsers/jsoncpp/archive/refs/tags/0.10.7.zip
  1. boost_1_58_0.tar.gz
    • 下载地址:http://sourceforge.net/projects/boost/files/boost/1.58.0/boost_1_58_0.tar.gz
  1. release-1.8.1.tar.gz (GoogleTest)
    • 下载地址:https://github.com/abseil/googletest/archive/release-1.8.1.tar.gz

步骤 3:修改配置与脚本

按照【第三节】的说明,修改:

  1. CMakeLists.txt:注释掉 -m32-m64 相关行。
  1. build.sh
    • 修改 BuildBoost 函数中的 ./b2,加入 architecture=arm address-model=64
    • 修改 BuildOpenSSL 函数中的 ./config,加入 no-asm -fPIC,且把 CFLAGSCPPFLAGS 移至前置环境变量位置。

步骤 4:清理缓存并执行编译

若之前有失败的编译尝试,必须先清理缓存,否则会残留非 PIC 格式的目标文件:

# 清理旧的编译残留
rm -rf tmp_down_dir/openssl-1.1.1d
rm -rf tmp_build_dir tmp_packet_dir bin

# 运行编译
chmod +x build.sh
./build.sh 2>&1 | tee build_detail.log

等待编译结束,提示 Build success without executing unit tests.bin/ 目录下生成 librocketmq.so 即可。

步骤 5:部署与 Python 导入验证

将生成的动态库部署到系统级动态链接库搜索路径:

# 1. 拷贝动态库
cp bin/librocketmq.so /usr/lib64/

# 2. 刷新共享库缓存
ldconfig

通过 Python 执行测试:

/opt/py/bin/python3 -c "import rocketmq.client; print('Success! Client file:', rocketmq.client.__file__)"

若正常打印出 Success! Client file: ...,则表示在 ARM64 麒麟系统下适配 RocketMQ 4.x 的工作完全成功!


五、 补充建议

不同版本的操作系统和 GCC 编译链在编译旧版本 C++ 代码时,可能会报出形态各异的环境错误。如果在实际编译中途遇到报错,强烈建议直接将当前编译失败的具体报错日志、以及 rocketmq-client-cpp 目录下的 build.sh 脚本文件一并丢给 AI 助手,让 AI 结合脚本的执行逻辑与具体报错信息综合分析。通常这种人机协作的调试方式能够实现秒级的精准定位,极大地缩短排错时间。

更多推荐