
1. 启动日志里那行黄字到底算不算非改不可1.1 先看清楚日志原文最近帮一个朋友排查 Tomcat 部署问题启动日志里又看到了那句几乎每次都能碰到的提示信息 [main] org.apache.catalina.core.AprLifecycleListener.lifecycleEvent The APR based Apache Tomcat Native library which allows optimal performance in production environments was not found on the java.library.path: [...]这就是大家常说的找不到基于APR的Apache Tomcat本机库。它由 Tomcat 启动阶段的AprLifecycleListener组件触发目的只有一个尝试加载基于 APRApache Portable Runtime的本地代码库也就是 Tomcat Native Library日常称呼里的 tcnative。如果 JVM 在搜索路径里找不到这个库它就把这句话写进日志然后继续后面正常的启动流程。日志里出现的java.library.path是 JVM 寻找本地库的搜索路径列表启动时会被完整打印出来一般会包含jre/lib、部分系统目录以及运维人员通过-Djava.library.path额外指定的目录。这句话的准确含义是JVM 在所有已配置的路径里都没有找到 tcnative 这个本地库所以 Tomcat 会放弃 APR 能力改用纯 Java 的实现继续运行。1.2 警告和错误边界先别慌但要清醒先给一个明确结论这不是会让 Tomcat 启动失败的错误。应用照常能起来接口照常能访问业务一行代码都不用改。很多人第一次在部署环境看到它第一反应是环境坏了、依赖缺失其实方向反了。但能跑和跑得舒服是两回事。官方那句 which allows optimal performance in production environments 说得很直接——这是生产环境达到最优性能所需的本地库。也就是说你现在的 Tomcat 不是不能干活而是没有把它的全部能力拿出来。压力上来之前可能感觉不明显一旦进入高并发、大流量、静态资源密集或 TLS 加密密集的场景差异就会从监控曲线里冒出来。1.3 为什么开发环境天天见生产环境也常见被问得最多的一个问题是为什么我新下载的 Tomcat 一启动就是这样原因在于 Tomcat 官方发布包是一个跨平台发行版它没法把所有操作系统的本地库预编译好塞进去所以默认不携带 tcnative。$CATALINA_HOME/bin目录下只有一个tomcat-native.tar.gz源码包需要根据当前操作系统自己编译安装。Windows 上虽然有预编译二进制包但那是独立下载页面里的内容不在主安装包里。开发环境里用 IDEA、Eclipse 启动项目时绝大多数人不会专门去配 tcnative所以这行提示在本地几乎是必现的。生产环境则往往是从开发环境直接复制配置或沿用旧脚本上线的有些人从装好那天起就没注意过这句日志这才是最可惜的地方——明明十分钟能解决的问题拖到了压测甚至故障复盘时才被翻出来。2. APR和Tomcat Native是怎么串起性能链条的2.1 APR是Apache生态的公共底座APR 全称 Apache Portable Runtime最初是为了让 Apache httpd 在不同操作系统上拥有一致的底层能力而诞生的 C 语言运行时库。它把 socket、进程、文件、内存池、线程这些系统级能力做了一层统一封装让上层应用不必关心当前跑在 Linux 还是 Windows 上。几十年开源项目持续迭代下来APR 在并发模型、内存管理、文件传输方面的表现已经非常成熟。很多服务组件并不直接暴露 APR但底层都依赖它来获得稳定的系统调用能力。Tomcat 瞄准的正是这部分积累。2.2 Tomcat Native是Java和C之间的那座桥Tomcat 主体是 JavaJava 不能直接调用 C 库中间必须有一层 JNI 桥接。Tomcat Native Library也就是 tcnative就是这座桥。tcnative 对外提供 JNI 接口底层把 APR 的 socket、poll、内存池等能力暴露给 Tomcat。Tomcat 拿到这些能力之后网络连接的处理、文件读写的路径、TLS 加解密的执行位置都会从纯 Java 层下沉到更接近操作系统的 C 层。Connector 不再是用 Java 模拟系统调用而是直接调用系统能力。2.3 APR装上之后Tomcat到底快在哪静态文件传输走 sendfile数据从文件到网卡几乎不经过用户态拷贝大文件响应时 CPU 消耗明显下降。内存管理更稳APR 用自带的内存池体系来管理连接和缓冲区比 Java 层频繁创建对象、触发 GC 要平顺长连接场景体感尤其明显。TLS/SSL 硬件加速APR 原生集成 OpenSSL握手和加解密都在 C 层完成。只要 CPU 支持 AES-NI 指令集加速能力是直接可用的Java 侧不需要折腾各种 JCE Provider。连接事件处理更接近内核底层事件循环经过 Apache 多年优化连接数多、短连接频繁时比纯 Java 实现能扛更大的量。2.4 版本对应关系别搞错tcnative 有 1.2.x 和 2.0.x 两条线。老项目里最常见的是 1.2.x运行在 Tomcat 8.5 和 9.0 上从 Tomcat 9.0.30 开始2.0.x 也可以用了Tomcat 10.x 则建议直接用 2.0.x。具体版本要以官方下载页标注和 release notes 为准不要随手拿一个 DLL 或.so就往服务器上丢。Tomcat 版本常用 tcnative 版本说明8.5.x1.2.x历史存量环境大量使用9.0.x1.2.x / 2.0.x9.0.30 之后可选 2.010.0.x / 10.1.x2.0.x新项目优先 2.02.5 没有APR不是不能跑但容量上限不一样说实话中小流量的内部管理系统NIO 和 APR 用起来体感区别不大没必要为了指标好看去折腾。但从压测和线上运维来看差异集中在三个场景大量静态资源请求、TLS 加密流量、连接数很高的长连接业务。这几个场景下APR 的 CPU 占用和响应时间通常更优。不装 APR 不丢人装了 APR 是给未来留容量余量。尤其是已经规划了 HTTPS 全站加密的项目APR 带来的 OpenSSL 硬件加速能力不是 Java 层调调参数就能追上的。3. 先别急着下载DLL花十分钟做一轮系统排查拿到这行日志先别急着下载 DLL 或编译源码。先判断清楚你的环境属于哪种情况是根本没装还是装了但加载失败。两个根因的处理方向完全不同方向错了会白折腾很久。3.1 确认Tomcat版本与JVM位数先用java -version确认 JVM 是 32 位还是 64 位。这在 Windows 上尤其重要因为 tcnative 的 DLL 必须和 JVM 位数严格一致。64 位 JVM 配一个 32 位 DLL加载时会直接报错日志看起来就像文件不对。同时确认 Tomcat 大版本。不同版本对 tcnative 的版本要求有差异这一步决定了后面去下载哪个版本。3.2 把启动日志完整读一遍not found和unable to load是两码事was not found on the java.library.path压根没找到库要么没放文件要么路径没配置。Unable to load library: tcnative-1.dll找到了文件但加载失败常见根因是缺 VC 运行库、位数不对、依赖的 APR 版本过低。把日志往下翻几行往往能看到更多细节很多人只看第一句就停下忽略了后面的真正原因。3.3 顺着java.library.path找答案Tomcat 启动日志会把这个搜索路径列表完整打出来。你把列表逐项看一遍就知道应该把 DLL 放到哪个目录或者需要在启动参数里额外指哪个目录。Linux 下还有另一条重要线索LD_LIBRARY_PATH环境变量。libtcnative-1.so能不能在运行期被找到很大程度取决于这个变量。可以用echo $LD_LIBRARY_PATH检查当前值也可以直接ldconfig -p | grep tcnative看系统缓存里有没有这个库。3.4 Windows和Linux的排查差异Windows 上的排查重点是 DLL 放置目录、PATH、VC 运行库、以及位数匹配。Linux 上的排查重点是编译是否成功、.so是否被ldconfig缓存、APR 版本是否够新。两者差异很大所以下面的实操也会分开写。3.5 常见错误现象对照表日志 / 现象常见根因解决方向was not found on the java.library.path库文件缺失或路径没配置放置库文件并配置搜索路径Unable to load library: tcnative-1.dllDLL 位数与 JVM 不一致或缺 VC 运行库安装对应 VC 运行库核对位数libtcnative-1.so: cannot open shared object fileLD_LIBRARY_PATH 未生效配置环境变量必要时执行 ldconfigAPR version x.y.z is not supported系统 APR 版本过旧升级 APR 或使用新版本 tcnative 源码编译4. Windows环境实操把tcnative-1.dll放到该放的位置4.1 预编译二进制方案省心省事Windows 上我不建议自己编译 tcnative。Tomcat Native 官方下载页提供了 Windows 预编译二进制包直接按 Tomcat 版本和 JVM 位数选一个下载解压后拿到tcnative-1.dll即可。放置方式有两种把tcnative-1.dll放到%CATALINA_HOME%\bin目录下。放到任意目录然后把该目录加入系统 PATH。如果你配置文件非常讲究、不想动系统 PATH也可以走setenv.bat方案。在%CATALINA_HOME%\bin\setenv.bat中写入set JAVA_OPTS%JAVA_OPTS% -Djava.library.pathC:\tomcat\binsetenv.bat是 Tomcat 启动脚本自动加载的扩展文件这样配置比直接改catalina.bat干净Tomcat 升级后配置依然保留。4.2 文件放对了还报错多半是VC运行库和位数Windows 下加载 DLL 最容易踩两个坑我几乎每次帮人排查都能碰到其中一个。第一个是位数不匹配。32 位 JDK 必须配 32 位 tcnative64 位 JDK 必须配 64 位 tcnative。下载页里的文件名通常会标注 x86/x64看清了再拿。第二个是缺少 VC 运行库。很多预编译 DLL 依赖微软的 MSVC 运行时目标机器上没装的话文件放得再对也加载不了。解决方式很直接去微软官网下载对应版本的vc_redist.x64.exe或vc_redist.x86.exe双击安装后重启 Tomcat。4.3 开发环境IDEA/Eclipse怎么处理本地开发调试时看到这行日志直接忽略也没问题。但如果你希望本机行为和生产环境保持一致尤其是要复现一个和线上相关的连接器问题那就需要让 IDE 里的 Tomcat 也加载 tcnative。在 IDEA 中找到 Run/Debug Configurations在 Tomcat Server 的 VM options 里加上-Djava.library.pathC:\tomcat\binEclipse 的 Server Runtime Environment 配置里同样可以在启动参数中指定java.library.path。配完记得重启 IDE 让新参数生效否则表面上配置了、运行进程里却没加载。4.4 验证是否加载成功重启 Tomcat如果日志里出现类似下面这一行说明 tcnative 已经加载成功信息 [main] org.apache.catalina.core.AprLifecycleListener.lifecycleEvent Loaded APR based Apache Tomcat Native library 1.2.34 using APR version 1.7.0.紧跟着ProtocolHandler 的启动行如果变成http-apr-8080就证明连接器真的跑在 APR 协议上了。如果还是http-nio-8080那是另一个问题后面单独讲。5. Linux环境实操从编译到环境变量固化Linux 下标准做法是源码编译。Tomcat 官方 zip 解压后bin目录里自带的tomcat-native.tar.gz就是 tcnative 源码包不需要去外网额外找源码。5.1 先把编译依赖装齐不同发行版的包名不一样CentOS / RHEL 系列yum install -y gcc make openssl-devel apr-develUbuntu / Debian 系列apt-get install -y build-essential libssl-dev libapr1-dev没有apr-devel或libapr1-dev后面 configure 阶段会直接卡住所以这一步别省。5.2 编译安装的完整命令cd $CATALINA_HOME/bin tar -xzvf tomcat-native.tar.gz cd tomcat-native-1.2.34-src/native ./configure \ --with-apr/usr/bin/apr-1-config \ --with-java-home/usr/local/jdk1.8.0_202 \ --with-sslyes \ --prefix/usr/local/apr make make install如果 JDK 就在标准位置且能被自动发现--with-java-home也可以不写。但装上多个 JDK 的环境建议显式指定避免 configure 探测到错误版本。安装成功后/usr/local/apr/lib下会生成libtcnative-1.so这才是运行期真正要加载的本地库。5.3 LD_LIBRARY_PATH和setenv.sh的组合编译安装只是第一步运行期找不到.so一样白搭。JVM 加载本地库时会参考java.library.path而它默认会读取LD_LIBRARY_PATH所以推荐在$CATALINA_HOME/bin/setenv.sh里固定export LD_LIBRARY_PATH/usr/local/apr/lib:$LD_LIBRARY_PATH export CATALINA_OPTS$CATALINA_OPTS -Djava.library.path/usr/local/apr/libsetenv.sh和 Windows 下的setenv.bat类似都是启动脚本自动加载的扩展文件。把环境变量写在这里比直接改catalina.sh、比把变量写进/etc/profile都更可控也不会因为一台机器上跑多个 Tomcat 而互相干扰。5.4 编译期和运行期我实际踩过的坑configure: error: can not find APR缺apr-devel编译器找不到apr-1-config装上依赖重跑。编译通过、启动仍报 not found先echo $LD_LIBRARY_PATH确认是否真的写进去了再看是不是/usr/local/apr/lib不在里面。精简容器镜像里没有 gcc、make编译前先装基础工具等编译完成再决定要不要保留编译依赖。--with-sslyes没生效时TLS 握手会退回 JSSEOPENSSL 的加速优势就发挥不出来。装完检查一下启动日志里是否出现 OpenSSL 相关成功信息。6. 不只是不报错让Connector真正跑在APR协议上6.1 怎么确认APR已经生效很多人在这一步会误判。日志里出现了Loaded APR based Apache Tomcat Native library就以为万事大吉其实这只能代表 tcnative 加载成功了不代表 Connector 真的在用 APR 协议。判断依据是接下来 ProtocolHandler 的启动行信息 [main] org.apache.coyote.AbstractProtocol.start 开始协议处理句柄 [http-apr-8080]注意这里要看到的是http-apr-8080而不是http-nio-8080。如果是http-nio说明 APR 库虽然加载了但 Connector 并没有切到 APR 实现。遇到这种情况先确认 Tomcat 进程是否正在按预期运行用ps -ef | grep tomcat看进程参数再回配置文件检查 protocol 怎么写。6.2 Connector协议怎么显式配置在 Tomcat 8.5 / 9 / 10 里protocolHTTP/1.1会在启动阶段根据 APR 是否可用自动选择实现tcnative 可用就走 APR不可用则退回 NIO。多数场景下默认配置就能自动选择但如果你想显式、强制指定可以写成Connector port8080 protocolorg.apache.coyote.http11.Http11AprProtocol connectionTimeout20000 redirectPort8443 /这里有一个容易踩的坑显式指定Http11AprProtocol之后如果 APR 库没装好Tomcat 启动会直接失败而不是静默退回 NIO。所以线上改配置之前务必把库先装好、验证好再动 Connector。6.3 APR下的TLS配置变化APR 模式下TLS 配置比 NIO 模式更像 nginx 的习惯。可以直接把证书链和私钥以 PEM 文件形式交给 OpenSSL不需要把一个 Java keystore 塞得满满当当Connector port8443 protocolorg.apache.coyote.http11.Http11AprProtocol maxThreads200 schemehttps securetrue SSLEnabledtrue SSLCertificateFile/usr/local/apr/certs/server.crt SSLCertificateKeyFile/usr/local/apr/certs/server.key /相比 NIO 下keystoreFile、keystorePass反复倒腾这种配置对运维同学友好得多也让 OpenSSL 的硬件加速能力直接参与到握手过程中。如果你之前用 JSSE 方式配置 HTTPS迁移到 APR 后建议对照官方文档把证书格式一起理顺。6.4 升级Tomcat之后容易忽略的版本问题Tomcat 小版本升级tcnative 一般不用动但大版本升级一定要重新核对 tcnative 版本。比如从 9.0 一路升到 10.1如果继续沿用很老的 1.2.x 库可能出现 OpenSSL 版本不兼容或 API 对不上的问题启动阶段直接报错排查起来反而更累。升级前把官方对应版本的 tcnative 一起换掉能省掉大量奇怪问题。换完之后按照上面的验证方法重新确认Loaded APR based Apache Tomcat Native library和http-apr-8080两行日志都出现了才算真正完成。7. 最后我的一点经验如果你只是本地开发看到那行提示可以直接忽略没必要为 IDE 里的调试进程专门装一遍 tcnative。但只要是准备上生产我建议把 APR 的安装写进部署步骤里当成一次性环境初始化来做而不是等压测发现 CPU 偏高后再回头补。我自己养成的习惯是装完 APR 后把Loaded APR based Apache Tomcat Native library这行日志写进启动检查清单每次部署后都搜一遍。没有这行不代表服务有问题但有了它我才会对高并发和 TLS 场景更有底气。整个操作在 Windows 上五分钟、在 Linux 上半小时内完成相对它带来的性能余量这笔投入非常划算。