ARTICLE DETAIL

资讯详情

深耕郑州网站建设与运营推广的一线实战洞察。

Linux服务器Tomcat安装配置与JDK环境变量实践指南

Linux服务器Tomcat安装配置与JDK环境变量实践指南 我印象里每次带新人部署Java Web项目到了Tomcat这一步多少都会卡一下壳。网上教程一搜一大把但很多都停留在“解压、启动、访问”三步曲的层面真到生产环境里要配JDK、调内存、设自启动时反而没有系统性的说明。这篇文就开始于这样一个想法把一次完整的Linux环境下的Tomcat安装配置过程用“为什么这么做”的角度重新梳理一遍。里面拿实测过的命令和配置文件说事也把我在各种发行版上踩过的坑一并丢出来希望给刚入门的朋友一条直达的路径也帮已经在部署的老手填上那些常被忽略的细节。1. 环境准备与基本的安装思路在敲第一条命令之前先把准备工作做扎实。这里的核心不只是“在Linux上装一个Tomcat”而是装好后能稳定跑起来、能开机自启、日志可诊断、升级可维护。所以本节的思路会按照“判断服务器基础环境 → 下载对应版本 → 规划安装目录 → 配置JDK”这条线展开。1.1 先确认服务器版本和系统架构再决定下载哪个包很多人一上来就下载最新版Tomcat结果放到老机器上跑不起来或者JDK版本完全不匹配来回折腾大半天。还不如先花一分钟把服务器底细摸清楚。cat /etc/os-release uname -m/etc/os-release会显示操作系统发行版名称和版本号比如 Ubuntu 22.04、CentOS 7.9、Debian 12 等。uname -m显示系统架构比如x86_64对应64位服务器aarch64对应ARM架构服务器。根据我自己的实践经验绝大多数云服务器和自建机都是x86_64但这两年ARM架构的机器越来越多。下载Tomcat二进制包时官方提供的tar.gz包是跨平台的所以架构对Tomcat本身影响不大真正需要留意的是JDK的版本它直接决定了Tomcat能不能正常启动。1.2 JDK版本与Tomcat版本的匹配关系差一个版本都可能报错Tomcat本质上是运行在JVM中的一个Java程序所以JDK版本不对Tomcat连启动都启动不了或者启动后报错“UnsupportedClassVersionError”。这一点对新手来说特别容易踩坑。这里给一份比较稳妥的对照表按照Tomcat官方文档和实测经验整理Tomcat版本最低JDK版本常用JDK版本说明Tomcat 9Java 8JDK 8 / JDK 11最经典的组合兼容性最好Tomcat 10Java 8JDK 11 / JDK 17注意包名从javax变成了jakartaTomcat 10.1Java 11JDK 11 / JDK 17 / JDK 21新项目建议直接上JDK 17Tomcat 11Java 17JDK 17 / JDK 21目前最新的大版本我在一台内存只有2G的云服务器上跑过Tomcat 10.1 JDK 17整体很流畅没必要刻意追新。对于生产环境来说稳定压倒一切选一个已经发布一年以上、社区反馈良好的版本比选最新的版本更稳妥。1.3 为什么我选择二进制tar.gz包而不是系统中自带的Tomcat这里有一个很容易被忽视的选择是直接用apt install tomcat9或yum install tomcat装系统源里的版本还是下载官方二进制包手动安装。我个人的建议是优先使用官方二进制包。原因有三点系统源里的Tomcat版本往往滞后比如CentOS 7自带的Tomcat还停留在7.x很多新的Servlet规范不支持。系统包安装方式会把配置文件分散在多个目录比如/etc/tomcat/、/usr/share/tomcat/对习惯“一个Tomcat目录搞定一切”的人来说很不方便。手动解压安装让你完全掌控目录布局、启动脚本和自启动配置排查问题时思路更清晰。当然如果是快速搭建一个临时测试环境那apt install tomcat9也够用。可一旦涉及生产部署、调整JVM参数、部署多个实例手动安装的自由度优势就很明显了。1.4 规划安装目录把软件集中管理后面维护省一半心有些朋友喜欢直接把Tomcat解压到/root/甚至/home/下这样做也不是说不可以但长期维护时你会发现非常乱。我习惯的目录规划是/opt/ ├── java/ │ └── jdk-17.0.x ├── tomcat/ │ └── apache-tomcat-10.1.x └── apps/ └── demo-webapp/将JDK和Tomcat都统一放在/opt/下好处是备份、迁移、版本升级都很方便。而且后面配systemd服务时路径清晰不容易写错。对比一下如果把Tomcat放在用户目录下一旦切换用户或者换人接管服务器可能连服务在哪儿都找不到。2. 下载解压与JDK环境变量配置准备工作和版本选型确认后开始正式安装。这一节的核心操作链路是下载JDK → 下载Tomcat → 解压并移动到/opt/→ 配置JAVA_HOME环境变量 → 验证Java命令是否生效。2.1 推荐使用OpenJDK并设置JAVA_HOME和PATH在Linux上安装JDK我首选OpenJDK。虽然Oracle JDK在某些特定场景下有优势但对于运行Tomcat来说OpenJDK完全够用而且开源免费、没有许可方面的顾虑。以JDK 17为例下载完解压后需要在/etc/profile中配置环境变量。这里要解释一下为什么这两个变量这么重要JAVA_HOME是给Tomcat的启动脚本找Java用的。Tomcat的startup.sh和catalina.sh会优先读取JAVA_HOME环境变量来定位java可执行文件。PATH变量是为了让你能在任何目录下直接执行java、javac命令方便命令行验证。打开配置文件vim /etc/profile在文件最末尾添加export JAVA_HOME/opt/java/jdk-17.0.10 export PATH$PATH:$JAVA_HOME/bin export CLASSPATH.:$JAVA_HOME/lib/dt.jar:$JAVA_HOME/lib/tools.jar保存退出后执行source /etc/profile java -version如果看到类似下面的输出说明环境变量配置成功openjdk version 17.0.10 2024-01-16 OpenJDK Runtime Environment (build 17.0.107) OpenJDK 64-Bit Server VM (build 17.0.107, mixed mode, sharing)关于环境变量我额外补充两点一是/etc/profile中对所有用户生效如果只想对当前用户生效可以改~/.bashrc二是修改完/etc/profile后已打开的终端需要重新登录或执行source才能生效这一点很多人第一次操作时会困惑以为是配置错了实际上只是没有刷新会话。2.2 下载Tomcat二进制包与校验JDK就绪后开始下载Tomcat。以Tomcat 10.1.19为例可以先用wget去官方站点拉包。这里我建议下载tar.gz格式不要下载.zipLinux环境下没那么多讲究tar.gz处理起来更干脆。cd /usr/local/src wget https://dlcdn.apache.org/tomcat/tomcat-10/v10.1.19/bin/apache-tomcat-10.1.19.tar.gz下载完成后一个被人忽略但很推荐的步骤是校验文件完整性。Apache官网每个发行包边上都提供了.sha512校验文件能用来确认下载的文件没有被篡改或者没有因网络原因损坏。sha512sum apache-tomcat-10.1.19.tar.gz然后与官网提供的.sha512文件内容比对一致的话再进入解压流程。实测中如果你所在的网络环境不稳定下载的包特别容易损坏最常见的结果就是解压到一半报错或者解压成功但启动时出现奇怪的类加载异常。所以别看多花这一步它能在后面省出不少排查时间。2.3 解压安装与建软链接校验通过后解压并移动文件tar -zxvf apache-tomcat-10.1.19.tar.gz mv apache-tomcat-10.1.19 /opt/tomcat/为了后续升级方便我通常会创建一个软链接比如/opt/tomcat/apache-tomcat-10.1.19指向一个叫apache-tomcat的链接目录ln -s /opt/tomcat/apache-tomcat-10.1.19 /opt/tomcat/apache-tomcat这样一来以后升级Tomcat时只需解压新版本然后改一下软链接指向即可配置文件、service脚本完全不需要动。这个操作算是老运维的常用手法但对刚接触Linux的同学来说可能还没养成这种习惯。2.4 理解Tomcat目录结构避免瞎找文件解压完Tomcat后会看到如下目录bin/ conf/ lib/ logs/ temp/ webapps/ work/每个目录的用途简单总结一下bin/存放启动和关闭脚本如startup.sh、shutdown.sh、catalina.sh。conf/是配置文件集中地server.xml、web.xml、context.xml都在这里。lib/存放Tomcat运行所需的jar包比如servlet-api.jar。如果是部署的应用需要额外的JDBC驱动通常也会放到这里而不是塞进单个应用的WEB-INF/lib中。logs/日志默认输出目录catalina.out、localhost_access_log都在这。webapps/默认的应用部署目录将war包丢进去后Tomcat会自动解压并部署。work/存放JSP编译后的临时class文件一般不用手动管。很多新手配置时总喜欢去翻web.xml找端口实际上端口配置在conf/server.xml里这个要记清楚。2.5 首次启动与验证在启动Tomcat之前如果当前登录的是root用户建议先切换到普通用户或单独创建一个tomcat运行用户。直接以root跑Tomcat虽然能启动但存在安全隐患一旦Tomcat被攻破攻击者继承的就是root权限。创建一个专门的tomcat用户是个好习惯。useradd -r -s /bin/false tomcat chown -R tomcat:tomcat /opt/tomcat/其中-r表示创建系统用户-s /bin/false表示该用户不能登录shell。这是我能想到的最安全且省心的方式。然后切换用户启动su -s /bin/bash tomcat -c /opt/tomcat/bin/startup.sh启动成功后查看监听端口ss -tnlp | grep java正常情况下至少能看到8080端口监听表示Tomcat已经启动。然后在浏览器访问http://服务器IP:8080就能看到默认首页。如果访问不了检查云平台安全组是否放行了8080端口这个问题在真实环境里特别常见明明Tomcat进程在跑外部却总是连不上十有八九是安全组没放行。3. server.xml核心配置深度解析既然已经能启动了但不意味着配置已经完成。真正用于生产环境的Tomcat还需要调整server.xml中的多个参数让它在并发、内存、访问日志等方面都更符合实际需求。这一节逐个拆解核心配置项并结合实际场景告诉你每个参数应该怎么调。3.1 Server端口与Connector端口的作用别再搞混了打开conf/server.xml第一眼就能看见两个端口相关的配置。不少初学朋友会混淆它们的职责。第一个是Server port8005 shutdownSHUTDOWN这个端口用于关闭Tomcat。你可以把它理解成是“控制台通道”通过向该端口发送指定字符串默认是SHUTDOWNTomcat就会安全关闭。这个端口生产环境建议改掉而且那条shutdown字符串也建议改成一个复杂点的值避免被外部的人直接用默认值关闭服务。修改示例如下Server port-1 shutdownTOMCAT_STOP_2026如果改成port-1表示禁用该端口只能用shutdown.sh脚本来停止服务也相对安全。我个人在多个生产环境里采用过这种配置效果很好既没有耽误过正常关闭操作也让外网扫描工具少了一个攻击点。第二个是Connector port8080 protocolHTTP/1.1 ...这是真正对外提供HTTP服务的端口。如果服务器上还有Nginx通常会改成非80端口让Nginx监听80并反向代理到Tomcat。3.2 最大线程数与队列长度的设置逻辑在Connector配置里这几个参数几乎是必调的Connector port8080 protocolHTTP/1.1 connectionTimeout20000 redirectPort8443 maxThreads200 acceptCount100 minSpareThreads10 /maxThreadsTomcat能够创建的最大请求处理线程数。默认200对于多数中小型Web应用来说是够用的。如果并发请求量很大可以提高到400左右但不要无限加大因为每个线程都会占用栈内存线程过多时GC压力和上下文切换开销也会上升。acceptCount当所有请求处理线程都处于忙碌状态时新的连接请求会被放入队列等待。这个参数就是等待队列的长度。如果设得太小突发流量下请求会被直接拒绝设得太大大家排队等久了前端代理又会判定超时。connectionTimeout建立TCP连接后等待客户端提交请求数据的超时时间默认是20000毫秒即20秒。对普通Web服务来说这个值基本够用为了防止慢速连接攻击可以适当调低到10000或15000。这里分享一个我实际调优过的场景一个面向C端用户的活动页服务平时QPS大概在800左右使用的是8核16G的机器。最初用的默认参数一到晚间高峰就会出现请求堆积、RT上升的报警。后来将maxThreads从200提高到400并将acceptCount从100提高到200问题就缓解了。但这里也想给出一个提醒线程数调大只是水平提升如果单个请求本身的处理逻辑很慢比如慢SQL、同步调用第三方接口线程再多也无用需要从业务应用层面优化。3.3 访问日志的启用与格式定制访问日志对于问题排查、安全审计、数据分析都非常重要。默认情况下Tomcat没有启用访问日志需要手动打开conf/server.xml中注释掉的那一段配置。启用后的标准配置如下Valve classNameorg.apache.catalina.valves.AccessLogValve directorylogs prefixlocalhost_access_log suffix.txt pattern%h %l %u %t quot;%rquot; %s %b /directory日志存放目录相对Tomcat根目录。prefix和suffix文件名前缀和后缀。pattern日志格式。%h是客户端IP%t是访问时间%r是请求行方法路径协议%s是状态码%b是响应字节数。生产环境中我往往会去掉%l逻辑用户名通常无意义并加一个%D来记录请求处理耗时这样排查慢请求非常直观pattern%h %t %r %s %b %D3.4 部署多个应用host name和appBase的关系默认情况下应用都放在webapps/目录下访问URL是http://IP:8080/应用名。如果你的服务器上需要跑多个应用有两个常见思路第一种在同一个Host下把每个应用放在webapps/下的不同子目录中比如webapps/ ├── app1/ │ └── WEB-INF/ ├── app2/ │ └── WEB-INF/这种结构部署方便但所有应用共用同一个JVM内存。如果一个应用内存泄露可能导致整个Tomcat挂掉。第二种为每个应用创建一个独立Host通过域名来区分。比如在server.xml中新增Host nameapp1.example.com appBase/opt/apps/app1 unpackWARstrue Context path docBase/opt/apps/app1/webapp / /Host这样一来不同域名访问不同应用逻辑隔离更彻底也方便单独重启某个应用。这种方式在小型私有部署里不常见但对多租户部署场景非常合适。我曾经给一个SaaS项目做过这样的结构后期维护时确实很方便每个客户的故障影响范围被控制在独立Host内不会殃及池鱼。3.5 JVM内存参数的设置别再一上来就Xmx512mTomcat默认的JVM内存参数可能偏小尤其是在大型Web应用或频繁GC的场景下。内存参数的调整通常放在bin/setenv.sh如果该文件不存在自行创建比直接改catalina.sh更规范。JAVA_OPTS-Xms512m -Xmx1024m -XX:MetaspaceSize128m -XX:MaxMetaspaceSize256m-XmsJVM初始堆内存大小。-XmxJVM最大堆内存大小。生产环境建议初始值和最大值保持一致这样可以避免运行过程中堆大小反复扩容收缩带来的性能损耗。-XX:MetaspaceSize元空间初始大小。如果应用用了比较多的动态代理、CGlib元空间容易涨可以适当调大。-XX:MaxMetaspaceSize元空间最大值防止无限增长导致内存耗尽。这里也给个直观的参考如果服务器物理内存是4GTomcat独占建议设置-Xms1024m -Xmx2048m如果服务器上同时跑着MySQL和Nginx那Tomcat的堆内存最好控制在1G左右免得大家抢内存。关于为什么“初始堆和最大堆设成一样”刚接触JVM的朋友可能不太理解。其实道理很简单如果初始堆小、最大堆大JVM会在请求量上涨时不断扩容堆内存这个扩容过程会触发Full GC造成应用停顿。设置成一样后堆内存从一开始就固定到位运行更平滑。4. 设置开机自启动与systemd服务管理把环境配好、配置调好其实只完成了一半。Linux服务器动不动重启如果每次都要人肉去startup.sh既不专业也很容易忘记。这一节讲讲如何让Tomcat作为系统服务开机自启以及为什么我推荐用systemd而不是写rc.local。4.1 为什么不建议在rc.local里写死启动命令很多老教程喜欢在/etc/rc.local里加一行/opt/tomcat/bin/startup.sh这在旧系统上确实可行。但rc.local这种方式有两点明显的坑一是在有些新版本的Linux上rc-local服务默认是disabled导致写进去的命令根本不执行二是rc.local只能控制“启动”没法精细管理“停止”“重启”“状态查看”服务崩溃时也没有自动重启机制。后来我用systemd统一管理一台机器上的所有服务都以服务单元文件的方式存在逻辑清晰命令统一日志集中。下面这段是我在实际生产环境中使用过的服务文件写法你可以直接参考。4.2 编写tomcat.service单元文件在/etc/systemd/system/下新建文件tomcat.servicevim /etc/systemd/system/tomcat.service写入以下内容[Unit] DescriptionApache Tomcat Web Application Container Afternetwork.target [Service] Typeforking Usertomcat Grouptomcat EnvironmentJAVA_HOME/opt/java/jdk-17.0.10 EnvironmentCATALINA_PID/opt/tomcat/apache-tomcat/temp/tomcat.pid EnvironmentCATALINA_HOME/opt/tomcat/apache-tomcat EnvironmentCATALINA_BASE/opt/tomcat/apache-tomcat ExecStart/opt/tomcat/apache-tomcat/bin/startup.sh ExecStop/opt/tomcat/apache-tomcat/bin/shutdown.sh Restarton-failure RestartSec10 [Install] WantedBymulti-user.target需要强调的几个点TypeforkingTomcat的startup.sh脚本启动Tomcat后会立即返回真正的Java进程是后来fork出来的子进程所以必须用forking类型systemd才能正确跟踪主进程。CATALINA_PID指定PID文件位置方便systemd判断服务到底有没有起来。Restarton-failure如果进程异常退出systemd会在等待10秒后自动拉起。我自己在一次真实事故里深有体会某个深夜Tomcat因为内存溢出挂掉了如果没有这个配置第二天早上才会被发现业务会长时间中断。保存后依次执行systemctl daemon-reload systemctl enable tomcat systemctl start tomcatdaemon-reload是让systemd重新扫描服务文件新加的或改动过的服务都必须执行这一步否则systemd可能还是认旧配置。然后检查状态systemctl status tomcat如果看到Active: active (running)说明服务正常。以后日常运维就用下面几条命令足够覆盖90%的启停场景systemctl start tomcat # 启动 systemctl stop tomcat # 停止 systemctl restart tomcat # 重启 systemctl status tomcat # 查看状态 journalctl -u tomcat -f # 查看实时日志4.3 如果一台机器上要跑多个Tomcat实例有些场景下一台服务器需要跑多个Tomcat实例比如不同应用要求的JVM参数不一样或者Stage环境与生产环境需隔离。处理方式其实不复杂不要动默认CATALINA_HOME里的配置而是复制一份目录作为CATALINA_BASE。例如mkdir -p /opt/tomcat/instance1 cp -r /opt/tomcat/apache-tomcat/conf /opt/tomcat/instance1/ cp -r /opt/tomcat/apache-tomcat/logs /opt/tomcat/instance1/ mkdir -p /opt/tomcat/instance1/{webapps,temp,work}然后创建第二个service文件把CATALINA_BASE指向/opt/tomcat/instance1[Service] EnvironmentCATALINA_HOME/opt/tomcat/apache-tomcat EnvironmentCATALINA_BASE/opt/tomcat/instance1 ExecStart/opt/tomcat/apache-tomcat/bin/startup.sh同时修改instance1/conf/server.xml里的三个端口避免和第一个实例冲突。这样两台实例就共用一份二进制文件但配置完全独立。4.4 设置Tomcat控制台账号方便管理页面使用自带的manager页面时默认是无法登录的需要在conf/tomcat-users.xml里配置用户role rolenamemanager-gui/ role rolenameadmin-gui/ user usernameadmin password你自定义的强密码 rolesmanager-gui,admin-gui/在生产环境配置此账号时有一点需要特别提醒如果Tomcat暴露在公网manager页面的暴力破解风险很大。建议该页面通过Nginx做访问白名单限制或者绑定本机回环地址访问。我自己部署的站点基本都是只允许内网管理IP段访问/manager与/host-manager安全感和/manager页面暴露是完全两码事。5. 常见问题与排查思路这个部分把我在实际部署与维护中积累的一些典型案例、常见报错以及排查路径整理出来。没用长篇大论的分析而是用“现象 → 排查方向 → 解决方法”的速查格式方便大家按图索骥。5.1 Tomcat启动失败但页面无任何反馈最常见的原因是catalina.out或logs/catalina.date.log里有详细的异常栈。先不要盲目改问题第一步永远是看日志tail -100 /opt/tomcat/apache-tomcat/logs/catalina.out如果提示Cannot find /opt/java/jdk-17.0.10/bin/java说明JAVA_HOME配置错误或目录名不一致检查/etc/profile或 service文件中是否和环境变量对不上。如果提示Address already in use: JVM_Bind 0x...:8080端口被占用用ss -tnlp找出占用进程确认是否是之前的Tomcat残留进程直接kill掉即可。如果没有任何日志输出且进程退出的特别快大概率是JDK版本不对检查java -version是否和下载的版本一致。这个过程看似简单但现实里确实有一些朋友会卡住原因往往是环境变量配置写错后没有重新加载或者Tomcat目录权限不够没有生成日志文件。如果是后一种情况执行chown -R tomcat:tomcat /opt/tomcat/就能解决。5.2 Tomcat能访问但静态资源和图片经常404如果你把静态资源放在webapps/ROOT/目录下遇到404最优先排查的不是路径而是Tomcat的Welcome File配置。在conf/web.xml中有一段welcome-file-list基本上只会配一个默认首页。如果你的资源是全静态的那还好办如果是动态页面加静态资源混合就需要仔细看应用的URL路径而不是单纯依赖Tomcat默认的欢迎页。另一种常见原因是部署war包时解压过程不完整。把war包手动丢进webapps/后Tomcat会自动解压但如果应用比较大解压到一半被杀或被重启就会出现文件缺失的诡异现象。这种情况的处理方式其实很简单停掉Tomcat删除webapps/下对应的应用目录再把war包重新丢进去启动Tomcat让它重新解压。5.3 修改了server.xml但不生效修改server.xml后不管有没有收到生效信号都需要重启Tomcat才能加载全部配置。startup.sh不会热加载配置文件这一点和Nginx的reload不太一样。同理修改context.xml和web.xml也是一样。另外还有一种比较隐蔽的情况你改的文件并不是Tomcat实际读取的文件。比如多个实例复用一个CATALINA_HOME然后你改了/opt/tomcat/apache-tomcat/conf/server.xml但实际上你的实例用的是/opt/tomcat/instance1/conf/server.xml。这种情况下检查service文件里CATALINA_BASE指向哪里就能发现真正生效的文件路径。5.4 页面打开缓慢、频繁出现超时页面慢的问题要从多个维度去排查不能一上来就调Tomcat参数。第一步先确认瓶颈到底在不在Tomcat。如果应用本身查询数据库耗时很大那么不管Tomcat的线程配置怎么调请求还是慢。可以使用thread dump来看当前线程都在执行什么jstack tomcat_pid thread_dump.log其中tomcat_pid可以通过ps -ef | grep java找到PID。拿到线程快照后重点观察有没有大量的线程处于BLOCKED或WAITING状态以及清理线程里堆积的HTTP请求对应什么业务代码。第二步检查是否GC停顿过长。使用jstat命令jstat -gcutil tomcat_pid 1000 10如果FGC列的数字持续快速增长说明Full GC很频繁堆内存可能不够或者存在大量可以回收的垃圾对象。这时要结合JVM参数进行适度调整。第三步再看看访问日志里的%D耗时段。如果某些请求耗时明显偏长就顺着那条请求的URL去应用日志中定位具体环节。5.5 常见问题速查表症状可能原因快速处理方法启动即退出无日志JDK版本不匹配java -version确认版本对比Tomcat要求8080端口被占用其他进程占用端口ss -tnlp找到进程停掉或改端口外部无法访问8080云安全组/防火墙未放行检查安全组规则和firewalld/iptables配置配置了自启动但重启后没生效systemd enable未执行或服务文件有误执行systemctl enable tomcat并检查服务文件管理页面登录403未配置tomcat-users角色或IP限制配置tomcat-users.xml并根据部署环境限定访问来源部署war包无法访问解压失败或路径配置问题停止服务、删目录、重新部署war包日志中文乱码UTF-8编码问题设置JAVA_OPTS-Dfile.encodingUTF-8并保证文件编码是UTF-8OutOfMemoryError: PermGen space永久代内存不足JDK8以上使用Metaspace参数调整旧JDK则调-XX:MaxPermSize5.6 一个印象很深的线上事故排查之前遇到过一个比较典型的案例想拿出来说说。某应用的Tomcat运行正常一切看起来都很平顺但每晚凌晨2点左右服务就会自动“假死”——网络能连通但页面一直转圈。一开始以为是定时任务把CPU打满了top一看CPU占用很低内存也充足一时没有头绪。后来看了catalina.out日志发现日志在假死时间点一直在刷一个错误循环上万行内容全是java.io.IOException: Too many open files。这里的问题不是Tomcat配置而是Linux系统层面ulimit -n设置太小导致Tomcat打开文件句柄达到上限后无法继续响应新的请求。解决方案是把limits调大vim /etc/security/limits.conf追加tomcat soft nofile 65535 tomcat hard nofile 65535同时确认systemd服务文件里也加上对应的Limit设置LimitNOFILE65535这个案例给我留下的教训是Tomcat本身跑得再稳也逃不开Linux系统资源的约束。排查线上问题时视野不要局限在Tomcat日志本身系统层、网络层、文件句柄这些外部因素都要纳入考量范围。这也是为什么我在全文中反复强调“先看底层环境再做上层配置”的原因。6. 部署实践从上传war包到正式上线前面几节把Tomcat安装配置和问题排查都梳理了一遍这一节专门聊聊从拿到一个war包到让它正式对外提供服务完整流程里会经历的环节。这套流程我差不多每周都会走一两次按步骤来做基本不会出错。6.1 上传war包到webapps目录的注意事项项目工程构建出的war包一般通过scp、rsync或图形化SFTP工具上传到服务器。这里的问题不是怎么传文件而是传完之后放在哪里。线上环境建议先放在/tmp/或/opt/apk/暂存再移动到webapps/。避免大文件直接上传到webapps/时因传输中断造成半截文件。上传完成后先检查文件完整性最简单的方式是和服务端构建后的war包比对MD5md5sum /opt/apk/demo-webapp.war进入webapps开始部署cp /opt/apk/demo-webapp.war /opt/tomcat/apache-tomcat/webapps/拷贝进去以后Tomcat会自动检测到war包并解压部署。既然会自动解压那这里也是个高效的发布方式应用升级时直接覆盖同名war包Tomcat检测到时间戳变化会触发重新解压。6.2 ROOT应用映射去掉URL中的项目名默认情况下war包名为demo-webapp.war访问地址是http://IP:8080/demo-webapp/。如果想直接通过http://IP:8080/访问你的应用有几种方式来实现。第一种最直接将war包改名为ROOT.war放到webapps/下mv demo-webapp.war ROOT.war第二种方式利用Context配置将某个路径映射到ROOTContext path docBasedemo-webapp /方式一比较粗暴但直观适合只有一个应用的主机方式二更灵活适合有多个应用共存的场景。在真实环境中我倾向于在Nginx层将域名根路径转发给这个应用而不直接改Tomcat的Context这样可以减少Tomcat端的逻辑改动调灰度、切换目录关系时也更方便。6.3 验证部署成功的几个关键点完成上述步骤后不要光看Tomcat进程在就跑要确认应用本身是健康的。我执行部署后的基本检查序列如下检查Tomcat日志中是否出现Deployment of web application archive [xxx.war] has finished in [xxx] ms。使用curl直接在本机请求页面接口确认响应正常curl -I http://127.0.0.1:8080/查看logs/localhost_access_log是否记录了请求访问的IP和路径。如应用有健康检查URL直接请求/health并确认返回预期的状态码。这几步走得顺的话基本可以认定本次部署成功。如果哪一个环节不通顺着日志去排查远比盲目猜测要节约时间。7. 运维经验Tomcat和Nginx的协作方式虽然标题只讲Tomcat安装配置但实际生产环境中Tomcat很少单独以80端口对外提供服务绝大多数配合Nginx使用。这一节作为运维经验补充来讲毕竟只装好Tomcat而不会接入Nginx后续部署项目时还是会遇到跨域、静态资源、HTTPS证书等一系列问题。7.1 为什么要在前面加一层NginxNginx和Tomcat的角色分工很明确Nginx负责接收请求、处理静态资源、做负载均衡和HTTPS终结Tomcat只负责运行Java应用逻辑处理动态请求。静态资源CSS、JS、图片、音视频由Nginx直接返回完全不进Tomcat节省了大量Java线程资源。多个Tomcat实例时由Nginx通过upstream做负载均衡后端扩容时只需增加Tomcat节点并修改Nginx配置。HTTPS证书放在Nginx层Nginx解密后的明文请求再转发给内部TomcatTomcat本身无需处理复杂证书配置。一个很典型的配置片段是upstream tomcat_backend { server 127.0.0.1:8080 weight1 max_fails2 fail_timeout10; server 127.0.0.1:8081 weight1 max_fails2 fail_timeout10; } server { listen 80; server_name example.com; location / { proxy_pass http://tomcat_backend; proxy_set_header Host $host; proxy_set_header X-Real-IP $remote_addr; proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; } }加了这一层之后Tomcat的访问日志拿到的客户端IP会变成127.0.0.1因为真实IP藏在X-Forwarded-For头里。Tomcat要拿到真实IP的话需要在server.xml的Host中添加下面这个ValveValve classNameorg.apache.catalina.valves.RemoteIpValve internalProxies127\.0\.0\.1 remoteIpHeaderx-forwarded-for proxiesHeaderx-forwarded-by /这个配置绝对算得上实际运维中的高频坑点。很多朋友链路通了之后一看Tomcat日志里全是127.0.0.1还以为是没打印到正确字段实际上就是没加RemoteIpValve。7.2 用Nginx区分静态资源路径与动态请求路径对于小型Web应用可以不用把所有资源都交给TomcatNginx直接把/static/目录映射到磁盘上的文件location /static/ { alias /data/www/static/; expires 7d; add_header Cache-Control public; }这样静态资源请求完全由Nginx处理Tomcat只接收location /匹配到的动态请求处理效率和并发能力都能明显提升。这个方法不需要对代码做任何改造配置好路径即可。8. 后续升级与维护的注意事项到这一节Tomcat的安装配置、自启、问题排查、架构协作基本都讲完了。最后聊一聊版本升级和维护中容易忽略的细节。我觉得这一块才是区分“会装”和“会养”的关键所在。8.1 版本升级时别直接覆盖旧目录生产环境升级Tomcat版本最怕的是一步到位、就地覆盖。如果你直接解压新版本覆盖到旧目录轻则配置丢失重则因jar包不一致引发莫名其妙的问题。更安全的升级流程是停掉现有Tomcat服务。备份旧目录中改过的核心内容conf/目录、webapps/中的应用、lib/下的额外驱动包。解压新版本到新目录建立新的软链接。将备份的配置按需复制到新目录。启动新版本并检查日志和应用健康状态。确认没有问题后再决定是否删除旧目录。为了能快速回滚我会在升级前将旧的整个Tomcat目录打个压缩包tar -czf tomcat_backup_$(date %Y%m%d).tar.gz /opt/tomcat/apache-tomcat这个步骤成本极低但能带来很大的安全感。毕竟生产环境里一旦升级出问题快速恢复远比现场调试重要。8.2 日志文件切割与定期清理Tomcat日志如果长期不清理catalina.out可能会涨到几十G不仅占用磁盘还可能影响系统整体性能。最实用的清理方式是利用系统自带的logrotate按天切割在/etc/logrotate.d/下新建tomcat文件/opt/tomcat/apache-tomcat/logs/catalina.out { copytruncate daily rotate 14 compress missingok notifempty }各参数含义copytruncate先复制日志内容再清空原文件。这里特别说明如果不用copytruncate而用rename方式Tomcat持有的是旧文件句柄会继续往旧文件写导致切割不生效。所以使用Tomcat时酱配置中加copytruncate是必要的。daily每天切割一次。rotate 14保留14份日志。compress压缩旧日志。配置完成后可以先手动执行一次验证效果logrotate -vf /etc/logrotate.d/tomcatlocalhost_access_log天然按天命名由文件前缀加日期生成配合定期的日志清理任务即可。8.3 Tomcat监控的有效方式日常运维中对Tomcat保持基本监控能及时发现问题。除了传统的top、free、df命令之外还可以用JDK自带工具做些深度监控jps查看Java进程列表。jstack打印线程快照。jstat查看JVM内存与GC统计。jinfo查看JVM启动参数。如果你接入了Prometheus体系也可以使用jmx_exporter暴露指标配合Grafana展示JVM堆内存、GC次数、线程数变化。我自己用的比较多的是后者因为它能把监控数据沉淀下来发生故障时回看时间轴问题定位效率高很多。这里给一个不成熟的建议Tomcat默认自带的manager应用会暴露部署API如果开了远程访问要注意防护最好设置强密码并限制IP。这算是安全基础配置但仍然值得反复强调。写在最后Tomcat安装配置这条路我走过了从“解压即用”到“全链路调优”的完整过程。回看这些年踩过的坑大多数不是技术多难而是像JDK版本、端口占用、日志切割、自启动这类基础但容易忽略的细节。只有把每一个环节里“为什么这么做”想清楚了排障时才能不至于瞎猜一通。我个人觉得这篇文章里最有价值的不是每一条命令本身而是里面那些经历过真实业务验证的选择逻辑。生产环境不同于本地开发环境每一个改动都要考虑稳定性、可回滚性和后续可维护性。按这个思路去操作不管以后用的是Tomcat还是其他中间件这个底层的工程素养都是共通的。最后分享一个小技巧首次在一台新机器上部署Tomcat时可以按照“下载 → 校验 → 解压 → 配置JDK → 启动 → 查看日志 → 访问页面”这样一条流水线把每一步的结果都记录在案。之后的任何一次变更都以这套基线为参照。当排查问题和回溯变更时这份记录能节约非常多的时间——我自己就是这样走过来的所以特别推荐你也试试。
返回列表