[实战] 申威 SW64 适配 Nacos 2.x/3.x:RocksDB 8.8.1 编译、瘦身与完整打包指南
前言
在信创国产化替代的大背景下,将中间件迁移至 申威 SW64(sw_64) 架构是常见需求。Nacos 2.x 版本(1.4前没有)引入了 JRaft 协议,底层强依赖 RocksDB 进行数据存储。
然而,官方发布的 Nacos 包中内置的 RocksDB 动态库缺少申威的 librocksdbjni.so,在申威服务器上启动时会报 UnsatisfiedLinkError。此外,由于 SW64 架构特性,直接编译源码会因“无硬件计时器”报 No timer implementation 错误。
本文记录了在 SW64 + glibc 2.28 环境下,从源码编译 RocksDB 8.8.1,解决计时器报错,并通过 Release 模式与 Strip 优化 将动态库体积从 200MB+ 缩减至 20MB 左右,最后演示如何通过手动替换或Maven集成的方式,将其无缝替换进 Nacos,截止25.12.01,nacos2.5.x-3.x,都使用的RocksDB 8.8.1(2023年发布版本)
1. 环境信息
| 项目 | 值 | 说明 |
|---|---|---|
| CPU | sunway/(3231/WX-8000) | sw_64 架构 |
| OS | 麒麟 / 统信 UOS | 基于 glibc 2.28 |
| JDK | SWJDK 8u312 | 必须包含 javac 和 jni.h |
| RocksDB | 8.8.1 | Nacos 2.x/3.x 依赖版本(截止25.12.01) |
| GCC | 8.3.0+ | 支持 sw_64 指令集即可 |
| Maven | 3.6.0+ | 建议较新版本,用于依赖管理 |
注意问题:SW64 用户态没有可读的 Cycle Counter 寄存器,导致 RocksDB 默认计时逻辑在编译时就会报错,需手动补全,当然很久接触内核cpu之类的,可能是我没找到。
2. 准备编译环境
RocksDB 的 JNI 编译需要 JDK 开发包(Headers)以及一系列压缩库的开发包。
# 1. 安装 SWJDK 开发包 (确保有 javac)
sudo yum install -y java-1.8.0-swjdk-devel.sw_64# 2. 设置环境变量 (编译脚本强依赖 JAVA_HOME)
export JAVA_HOME=/usr/lib/jvm/java-1.8.0-swjdk-8u312.sw_64
export PATH=$JAVA_HOME/bin:$PATH# 3. 安装 C++ 编译工具链及压缩库依赖
# zlib, snappy, lz4, zstd 是 RocksDB 性能关键
sudo yum install -y gcc gcc-c++ make cmake \zlib-devel snappy-devel lz4-devel zstd-devel
3. 源码下载与 SW64 计时器补丁
RocksDB 内部使用 toku_time 获取高精度时间,源码中未适配 SW64。如果不修改,执行 make 时会直接报错 #error No timer implementation。
3.1 下载源码
wget https://github.com/facebook/rocksdb/archive/refs/tags/v8.8.1.tar.gz
tar xf v8.8.1.tar.gz
cd rocksdb-8.8.1
3.2 修复 No timer implementation (关键)
编辑文件:utilities/transactions/lock/range/range_tree/lib/portability/toku_time.h
在文件末尾的 #elif defined(...) 链条中,加入 SW64 的 clock_gettime 实现。
修改方法:找到 #else #error No timer implementation... 之前,插入以下代码:
// ----------------- 新增开始 -----------------
#elif defined(__sw_64__) || defined(__SW64__) || defined(__sw_64)// SW64 补丁:使用 clock_gettime 替代硬件计数器struct timespec ts;clock_gettime(CLOCK_MONOTONIC, &ts);// 返回纳秒级时间,符合 tokutime_t 特性return (uint64_t)ts.tv_sec * 1000000000ULL + (uint64_t)ts.tv_nsec;
// ----------------- 新增结束 -----------------#else
#error No timer implementation for this platform
#endif
4. 编译优化与瘦身(关键步骤)
很多同学编译出来的 librocksdbjni.so 动辄 300MB,这在生产环境是不可接受的。
原因分析:
- 未 Strip:包含了大量供 GDB 使用的调试信息。
- 静态链接:RocksDB 为了可移植性(Portable),将 Snappy/Zstd 等库静态打入了 so 中。
- Debug 模式:默认 CMake 配置可能未开启
-O3优化。
4.1 执行优化编译
我们显式指定 Release 模式,并在编译后执行 strip。
# 1. 创建构建目录
mkdir build-sw64-opt && cd build-sw64-opt
# 2. CMake 配置
cmake .. \-DCMAKE_BUILD_TYPE=Release \-DFAIL_ON_WARNINGS=OFF \-DWITH_JEMALLOC=OFF \-DWITH_SNAPPY=ON \-DWITH_LZ4=ON \-DWITH_ZLIB=ON \-DWITH_ZSTD=ON \-DPORTABLE=ON# 3. 并行编译核心库与 JNI 接口
# PORTABLE=1 确保生成的指令集通用性
# nproc代表多少个线程
PORTABLE=1 make rocksdbjava -j$(nproc)
4.2 手动瘦身 (Strip)
编译完成后,在 java/target/ 下会生成库文件(通常是 librocksdbjni-8.8.1-linux.so)。
cd ../java/target/# 查看原始大小
ls -lh librocksdbjni*.so# 【建议】方案 A:仅移除 Debug 信息(安全,保留符号表供 Java 调用)
strip --strip-debug librocksdbjni-8.8.1-linux*.so# 【激进】方案 B:移除所有符号(体积最小,可能丢失部分堆栈信息)
# sudo strip --strip-unneeded librocksdbjni-8.8.1-linux*.so# 再次查看大小,通常在 10MB - 20MB 之间,完美!
ls -lh librocksdbjni*.so
5. 两种适配路径:源码修改 vs 暴力替换
你可以选择 "正确" (修改 Java 源码) 或者 "邪道" (重命名库文件)。
路径一:源码级适配(推荐,彻底解决)
我们修改 RocksDB 的 Java 源码,让它完美支持申威架构。
1. 修改 Environment.java
文件:java/src/main/java/org/rocksdb/util/Environment.java
Step 1: 添加 isSW64() 方法
private static boolean isSW64() {// 申威架构在 Java os.arch 中通常返回 "sw_64",为了保险把 "sw64" 也加上return ARCH.equals("sw_64") || ARCH.equals("sw64");}
Step 2: 修改 getJniLibraryName 方法
public static String getJniLibraryName(final String name) {if (isUnix()) {final String arch = is64Bit() ? "64" : "32";if (isPowerPC() || isAarch64()) {return String.format("%sjni-linux-%s%s", name, ARCH, getLibcPostfix());}// ... (其他分支)// ----------------- 新增 SW64 分支 -----------------else if (isSW64()) {// 强制返回 "rocksdbjni-linux-sw_64"// 这样 NativeLibraryLoader 就会去拼成 librocksdbjni-linux-sw_64.soreturn String.format("%sjni-linux-sw_64", name);}// ----------------- 新增结束 -----------------else {return String.format("%sjni-linux%s%s", name, arch, getLibcPostfix());}}// ...
2. 修改 Makefile (可选)
为了让 make 自动生成的 so 文件名带上 sw_64 后缀:
# 找到判断 MACHINE 的部分,添加 sw_64
ifneq (,$(filter ppc% s390x arm64 aarch64 sparc64 loongarch64 sw_64, $(MACHINE)))ROCKSDBJNILIB = librocksdbjni-linux-$(MACHINE)$(JNI_LIBC_POSTFIX).so
elseROCKSDBJNILIB = librocksdbjni-linux$(ARCH)$(JNI_LIBC_POSTFIX).so
endif
3. 重新编译 JAR 包
# 回到 rocksdb 根目录
cd ../..
export JAVA_HOME=/usr/lib/jvm/java-1.8.0-swjdk-8u312.sw_64# 清理并重新编译
make jclean
make rocksdbjava -j$(nproc)
此时 java/target/ 下的 jar 包已经是包含 librocksdbjni-linux-sw_64.so 的完全体了。
路径二:暴力替换(重命名欺骗)
如果不想改代码,可以将编译出的 sw_64 so 文件重命名为 librocksdbjni-linux64.so。
原理:官方 JNI 加载器在匹配不到架构时,通常会回退尝试加载 linux64 的库。
- 将
librocksdbjni-8.8.1-linux.so重命名为librocksdbjni-linux64.so。 - 更新到 jar 包中:
jar uf rocksdbjni-8.8.1.jar librocksdbjni-linux64.so
6. Nacos 集成方案
方案 A:Maven 本地安装(最规范,适合重编译 Nacos)
如果你有 Nacos 源码,想重新打个干净的包:
# 1. 安装到本地 Maven 仓库
# 假设你在 rocksdb-8.8.1/java/target 目录下
mvn install:install-file \-Dfile=rocksdbjni-8.8.1.jar \-DgroupId=org.rocksdb \-DartifactId=rocksdbjni \-Dversion=8.8.1 \-Dpackaging=jar# 2. 编译 Nacos
cd nacos-source
mvn -Prelease-nacos -Dmaven.test.skip=true clean install -U
方案 B:Jar 包修改(最实战,适合已有 Nacos 包)
Nacos Server 是一个 Spring Boot Fat Jar,内部嵌套了多层 Jar 包。直接解压替换容易导致签名错误或包结构损坏,请严格按以下步骤操作。
假设:
- Nacos 安装包:
nacos-server.jar - 新编译的库:
rocksdbjni-8.8.1.jar(上面编译出来的)
步骤 1:解压外层 Jar
mkdir work_dir && cd work_dir
cp /path/to/nacos-server.jar .# 解压
jar -xf nacos-server.jar
# 此时目录包含:BOOT-INF, META-INF, org ...
步骤 2:定位并替换 RocksDB 依赖
RocksDB 的 Jar 包位于 BOOT-INF/lib/ 下。
cd BOOT-INF/lib/# 备份原版
mv rocksdbjni-8.8.1.jar rocksdbjni-8.8.1.jar.bak# 【关键】将我们编译好的 jar 拷贝进来
cp /path/to/your/compiled/rocksdbjni-8.8.1.jar .
补充:如果你只有 .so 文件,想手动塞进这个 jar:
mkdir temp_rocks && cd temp_rocks jar -xf ../rocksdbjni-8.8.1.jar cp /path/to/your/librocksdbjni-linux-sw_64.so . jar -cfM ../rocksdbjni-8.8.1.jar . cd .. && rm -rf temp_rocks
步骤 3:重新打包外层 Nacos Jar (必须注意压缩参数)
回到 work_dir 根目录。
cd ../.. # 重新打包为 nacos-server-sw64.jar
# -0 (数字0): 仅存储不压缩。Spring Boot 外层 Jar 推荐不压缩,提高启动速度,防止 Loader 报错
# -c 创建, -f 文件, -M 不生成新清单
jar -cfM0 nacos-server-sw64.jar .
7. 验证与总结
将生成的 nacos-server-sw64.jar 部署到申威服务器。
7.1 启动验证
sh bin/startup.sh -m standalone
7.2 日志检查
查看 logs/nacos.log,重点关注是否有以下报错:
UnsatisfiedLinkError: 说明 so 文件没加载到,或者名字不对。SIGILL(非法指令): 说明编译时没加PORTABLE=ON,或者用了错误的 GCC。
正常的话

7.3 无 VNC 环境验证 (Curl 测试)
在没有图形界面的情况下,使用 curl 验证 RocksDB 功能。
测试配置写入 (Write Test):
curl -X POST 'http://127.0.0.1:8848/nacos/v1/cs/configs' \
-d 'dataId=sw64-test' \
-d 'group=DEFAULT_GROUP' \
-d 'content=RocksDB_Works_On_SW64'
# 预期返回: ok之类
测试配置读取 (Read Test):
curl -X GET 'http://127.0.0.1:8848/nacos/v1/cs/configs?dataId=sw64-test&group=DEFAULT_GROUP'
# 预期返回: RocksDB_Works_On_SW64
避坑指南总结
- 必须补全代码:没有
toku_time的 sw_64 分支,RocksDB 无法编译。 - 一定要 Strip:不 strip 的库文件巨大,不仅占磁盘,Nacos 启动加载会非常慢。
- 打包参数:重打 Spring Boot Jar 时,务必使用
jar -cfM0,不要用 Zip 工具,否则大概率报Unable to find main class。 - Java 版本:编译 JNI 时,
JAVA_HOME必须指向 SWJDK 的 devel 包路径,否则找不到jni.h。