Tomcat 7.0.108 旧系统部署与排错实战
简介Tomcat 7.0.108 的完整发行压缩包面向需要本地运行、调试或学习 Java Web 应用的开发者与高校学习者可用于搭建 Servlet/JSP 运行环境、验证 WAR 包部署及研读容器配置。包内共 640 个文件涵盖 228 个 HTML 页面、106 个 class、78 个 Java 源文件、58 个 JSP、30 个 jar 包及 15 个 XML 配置并配有 bat/sh 脚本、properties 配置、TLD 标签库与示例 Web 应用等压缩包约 10.38MB目录组织接近标准 Tomcat 发行版。已有 634 人学习或下载说明其在同类基础环境资料中具有一定参考度。读者可据此观察 bin、conf、lib、webapps、logs 等目录的组织方式结合 server.xml、web.xml、Context 配置和日志文件理解应用部署与运行机制也可用于课程实验、项目联调和启动排查适合作为 Java Web 入门与容器配置查阅的轻量资料包。1. 老系统只认 Tomcat 7tomcat-7.0.108.zip 能救哪类现场某公司内网还有一套只认 Tomcat 7 的老系统war 包在 Tomcat 9 下不是 Servlet 初始化失败就是 JSP 编译报错把 tomcat-7.0.108.zip 解压到独立目录后同样的包一次就跑起来了。Tomcat 7.0.108 是 Tomcat 7 分支里的后期版本支持 Servlet 3.0、JSP 2.2、EL 2.2适合维护旧项目、复现历史环境、做课程实验不适合新项目直接上生产公网。它的价值不在“新”而在“对旧 war 包足够宽容”。很多老系统依赖javax.servlet包名、旧版 JSTL、旧 JDBC 驱动甚至依赖 Tomcat 7 的类加载顺序和 JSP 编译行为换到高版本就是玄学问题一堆。这个 zip 包解压后就是完整目录改JAVA_HOME、server.xml、webapps就能跑边界清楚适合想快速把旧环境拉起来的人。2. 拆开 tomcat-7.0.108.zip目录、环境变量与启动链路拿到压缩包后不要急着双击startup.bat。我见过最多的翻车不是 Tomcat 本身坏了而是解压到了带空格、带中文的路径或者机器上装了三个 JDKJAVA_HOME指向的版本和编译 war 的版本对不上。先把它解到一个短路径比如D:\apps\tomcat-7.0.108或/opt/apps/tomcat-7.0.108后面所有配置都围绕这个根目录展开。2.1 先认清七个目录别把 CATALINA_HOME 和 CATALINA_BASE 混了Tomcat 7 的目录不多但每个目录都对应一个排错入口。刚解压时先看一眼结构不要直接往lib里塞业务 jar也不要把日志写在webapps下面。常见目录职责如下目录作用容易踩的点bin启动、关闭、配置检查脚本Linux 下解压后可能没有执行权限confserver.xml、web.xml、context.xml、tomcat-users.xml改完不备份XML 写错直接启动失败libTomcat 级公共 jar数据库驱动放这里对 JNDI 数据源更稳logscatalina.out、localhost.*.log、访问日志只看控制台不看logs等于黑匣子temp临时文件权限不足时启动异常webapps默认应用部署目录旧 war 解压残留会覆盖新包workJSP 编译产物改 JSP 不生效时先怀疑这里CATALINA_HOME是 Tomcat 安装目录CATALINA_BASE是运行实例目录。单实例部署时两者可以相同多实例时CATALINA_HOME放公共程序CATALINA_BASE放各自的conf、logs、webapps、work。很多人只设了CATALINA_HOME却在另一个目录启动结果日志和配置全跑到默认位置排查时找不到文件。我一般会在启动脚本里显式打印这两个变量确认没有串。2.2 配 JAVA_HOME 与启动脚本Windows 和 Linux 各走一遍Tomcat 7 本身不捆绑 JDK启动脚本靠JAVA_HOME或JRE_HOME找 Java。旧项目常见组合是 JDK 7 或 JDK 8具体以 war 包编译版本和依赖库为准。不要用 JDK 17 去硬跑老 JSP模块化、反射访问、旧字节码都可能出问题。先确认java -version和javac -version一致再设置 Tomcat 的环境变量。Linux 下建议在bin/setenv.sh里写环境变量避免每次登录都手动 export。没有这个文件就新建权限设为可执行# bin/setenv.sh export JAVA_HOME/opt/java/jdk8 export CATALINA_HOME/opt/apps/tomcat-7.0.108 export CATALINA_BASE/opt/apps/tomcat-7.0.108 export PATH$JAVA_HOME/bin:$CATALINA_HOME/bin:$PATH # 给当前实例固定字符集减少 JSP 编译和日志乱码 export LANGzh_CN.UTF-8 export LC_ALLzh_CN.UTF-8这段脚本的作用是把 Java 和 Tomcat 的查找路径固定下来。JAVA_HOME必须指向 JDK 根目录不是bin目录CATALINA_BASE如果和CATALINA_HOME相同可以省略但显式写出来更方便多实例扩展。LANG和LC_ALL只影响系统层字符集不能替代 Connector 的URIEncoding后面还会单独配。Windows 下不要依赖系统环境变量碰运气可以在bin下新建setenv.bat或者在启动前用命令行设置REM bin\setenv.bat set JAVA_HOMEC:\java\jdk8 set CATALINA_HOMED:\apps\tomcat-7.0.108 set CATALINA_BASE%CATALINA_HOME% set PATH%JAVA_HOME%\bin;%CATALINA_HOME%\bin;%PATH% REM 避免控制台中文日志乱码按需设置 set JAVA_TOOL_OPTIONS-Dfile.encodingUTF-8setenv.bat会被catalina.bat调用适合放 JVM 参数和编码参数。JAVA_TOOL_OPTIONS会在启动时打印一行提示这是正常现象。不要把JAVA_HOME写到CATALINA_HOME里面也不要把 Tomcat 解压到Program Files这类带空格路径旧脚本对引号处理不如新版本稳。启动时先用前台模式不要一上来就startup.sh后台跑cd /opt/apps/tomcat-7.0.108/bin chmod x *.sh ./catalina.sh configtest ./catalina.sh runconfigtest会先检查server.xml的基本语法能提前拦掉大部分 XML 写错的问题。catalina.sh run把日志直接打到当前终端启动失败时不用去logs里翻。确认能起来后再用./startup.sh后台启动用./shutdown.sh关闭。Windows 下对应catalina.bat run和startup.bat双击闪退时打开 cmd切到bin目录执行catalina.bat run报错会留在窗口里。2.3 启动日志与关闭链路catalina.out 不是唯一要看的地方Tomcat 7 启动后logs下常见几类文件catalina.out收标准输出和标准错误localhost.YYYY-MM-DD.log收应用级日志localhost_access_log.YYYY-MM-DD.txt收访问日志。启动失败时先看catalina.out末尾再看localhost.*.log里有没有SEVERE。如果端口被占用catalina.out里会出现BindException如果 JDK 版本不对会出现UnsupportedClassVersionError如果server.xml写错configtest阶段就会报行号。关闭 Tomcat 不是杀进程就完事。shutdown.sh会连接server.xml里Server的关闭端口默认 8005发送SHUTDOWN字符串。关闭端口被改掉、被防火墙挡住、或者shutdown属性被改过shutdown.sh就会卡住最后只能kill。我一般会在改server.xml时把关闭端口也记下来别只记 8080。用ps -ef | grep tomcat确认进程真的退出避免旧进程占着 8080新进程起不来日志里却只报端口绑定失败。3. server.xml 调参8080 端口、线程池与编码一次配稳server.xml是 Tomcat 7 最常改的文件也是最容易改坏的文件。它管端口、连接器、Host、 Valve、关闭指令改错一个标签就可能起不来。我的习惯是先备份server.xml每次只改一类参数改完跑configtest再启动看日志。下面这套配置适合中小型旧系统NIO 连接器、200 个工作线程、UTF-8 URI 编码、访问日志带处理耗时。3.1 Connector 的 port、protocol 与线程参数怎么落Tomcat 7 默认的protocolHTTP/1.1在很多包中走 BIO连接数一高就容易被阻塞。旧机器如果 JDK 支持可以换成 NIO但要确认项目里没有依赖 BIO 的特殊行为。线程数不是越大越好maxThreads调到 500 而数据库连接池只有 20结果只是把压力堆在 Tomcat 里。下面是一段可落地的Connector配置!-- conf/server.xml 片段改前先备份 -- Server port8005 shutdownSHUTDOWN Service nameCatalina Connector port8080 protocolorg.apache.coyote.http11.Http11NioProtocol URIEncodingUTF-8 useBodyEncodingForURItrue maxThreads200 minSpareThreads20 acceptCount100 maxConnections5000 connectionTimeout20000 compressionon compressableMimeTypetext/html,text/xml,text/plain,text/css,application/javascript,application/json redirectPort8443 / Engine nameCatalina defaultHostlocalhost Host namelocalhost appBasewebapps unpackWARstrue autoDeploytrue Valve classNameorg.apache.catalina.valves.AccessLogValve directorylogs prefixaccess. suffix.log pattern%h %l %u %t quot;%rquot; %s %b %D / /Host /Engine /Service /Serverport是 HTTP 端口被占用时改成 8081 或 18080但要注意前端反向代理和防火墙规则同步改。protocol用 NIO 后Tomcat 7 的阻塞模型会变适合长连接和较多并发如果旧系统里用了某些依赖 BIO 线程模型的过滤器先小流量验证。maxThreads是同时处理请求的最大线程数minSpareThreads是保持的空闲线程acceptCount是等待队列长度队列满了会拒绝连接。connectionTimeout单位是毫秒20000 表示 20 秒。compression对文本资源有效图片和压缩包不要重复压缩。maxConnections在 Tomcat 7 后期版本可用限制瞬时连接数别把它和maxThreads混为一谈。3.2 URIEncoding 与 useBodyEncodingForURI中文参数不乱码的边界中文乱码最常见的现场是GET 参数乱码、POST 表单正常或者反过来。Tomcat 7 的 Connector 里URIEncodingUTF-8决定 URI 路径和查询串按什么字符集解码useBodyEncodingForURItrue表示 URI 查询串使用请求体的编码而不是固定用URIEncoding。这两个参数不是互相替代配错会出现“浏览器地址栏正常日志里乱码”的情况。旧项目里还常见request.setCharacterEncoding(UTF-8)只对 POST 体生效GET 查询串仍走 Connector 配置。我一般会同时做三件事Connector 设URIEncodingUTF-8过滤器里对 POST 设 UTF-8JSP 页面声明pageEncodingUTF-8。如果应用里有过滤器注意过滤器顺序编码过滤器要排在参数读取之前。还有一种情况是前端用application/x-www-form-urlencoded提交后端却按multipart/form-data解析参数根本进不到request.getParameter这时候不是编码问题是 Content-Type 不匹配别在server.xml里反复改。注意改完URIEncoding必须重启 Tomcat热加载不会让 Connector 重新初始化。旧连接池里的连接不受影响但新请求才会走新编码。3.3 Host 与 Valve访问日志和远程地址拿到真实值Host里的appBasewebapps决定默认部署目录unpackWARstrue表示 war 包会自动解压autoDeploytrue表示运行期检测新 war。旧系统如果要求“放 war 就生效”这两个开关要留着如果要求稳定建议关掉autoDeploy改成维护窗口重启。访问日志的pattern里%h是客户端地址%r是请求行%s是状态码%b是响应字节数%D是处理耗时微秒。前面有反向代理时%h可能拿到代理地址需要看代理是否传了X-Forwarded-For再决定是否加RemoteIpValve。不要盲目信任客户端传来的转发头内网代理可信才配。4. 部署 war 与上下文webapps、Context 描述符和 JNDI 数据源Tomcat 7 部署 war 有三种常见方式直接丢webapps、在conf/Catalina/localhost下写独立 Context 文件、在server.xml的Host里内嵌 Context。第一种最省事第二种最可控第三种最容易把server.xml改乱。旧系统维护我优先用独立 Context 文件war 包和配置分离升级时只换 war不动 Tomcat 主配置。4.1 三种部署方式的选择自动部署、外部 Context、server.xml 内嵌方式做法适合场景风险丢webapps把demo.war复制到webapps快速验证、单应用旧解压目录残留包名冲突独立 Context写conf/Catalina/localhost/demo.xml多应用、路径不在 webapps文件名必须和 context path 对上server.xml 内嵌在Host下写Context老教程常见改错主配置启动失败影响全部应用自动部署依赖Host的autoDeploy和unpackWARs放进去后看catalina.out是否出现Deploying web application archive。独立 Context 文件的文件名就是访问路径比如demo.xml对应/demo里面用docBase指向 war 或解压目录。server.xml内嵌方式不是不能用而是每次改都要动主配置旧机器上没有版本管理时很容易翻车。我一般会先把webapps/demo旧目录挪走再放新 war避免旧 JSP 和旧WEB-INF/lib残留。# 停应用后替换 war保留旧包做回滚 cd /opt/apps/tomcat-7.0.108 mv webapps/demo.war webapps/demo.war.bak.$(date %Y%m%d%H%M) cp /data/build/demo.war webapps/demo.war rm -rf webapps/demo work/Catalina/localhost/demo tail -f logs/catalina.out这段命令先备份旧 war再复制新包然后删掉旧解压目录和work下对应应用的 JSP 编译缓存。work/Catalina/localhost/demo不清改过的 JSP 可能还走旧编译结果。tail -f用来观察部署日志出现SEVERE就停下来不要继续叠配置。4.2 用独立 Context 文件控制 docBase 与 reloadable独立 Context 文件放在conf/Catalina/localhost/demo.xml内容可以控制docBase、reloadable、crossContext和资源。reloadabletrue方便开发期改类后自动重载但生产环境不建议开类加载器泄漏和内存增长很难查。crossContextfalse是默认值多个应用之间不要随便共享 ServletContext。下面是一个外部目录部署的例子!-- conf/Catalina/localhost/demo.xml -- Context docBase/data/apps/demo reloadablefalse crossContextfalse !-- 会话超时按分钟计旧系统常用 30 -- SessionCookie path/demo / /ContextdocBase可以指向解压后的应用目录也可以指向 war 文件。指向目录时升级只需替换目录内容指向 war 时Tomcat 会按unpackWARs决定是否解压。reloadablefalse表示不监控WEB-INF/classes和WEB-INF/lib变化改类必须重启换来的是稳定。SessionCookie path/demo限制会话 Cookie 的作用路径多应用同域时能减少串号。改完 Context 文件后Tomcat 通常会检测到并重新加载应用但生产环境我更愿意重启避免半加载状态。4.3 JNDI 数据源与 web.xml把连接池从代码里挪出去旧项目常见写法是把数据库账号密码写死在jdbc.properties一换环境就改包。Tomcat 7 支持在 Context 里配 JNDI 数据源应用通过java:comp/env/jdbc/demo查找。JDBC 驱动要放到 Tomcat 的lib下不要只放WEB-INF/lib否则容器级数据源可能找不到驱动。下面是一个 DBCP 数据源配置!-- conf/Catalina/localhost/demo.xml 中的资源片段 -- Context docBase/data/apps/demo reloadablefalse Resource namejdbc/demo authContainer typejavax.sql.DataSource driverClassNamecom.mysql.jdbc.Driver urljdbc:mysql://127.0.0.1:3306/demo?useUnicodetrueamp;characterEncodingUTF-8 usernamedemo_app passwordreplace_me maxActive50 maxIdle10 minIdle2 maxWait10000 validationQuerySELECT 1 testWhileIdletrue timeBetweenEvictionRunsMillis30000 / /ContextmaxActive是最大活动连接数要和数据库端max_connections一起看maxIdle是最大空闲连接minIdle是最小空闲连接maxWait是获取连接的最长等待毫秒数validationQuery用来检测连接是否可用testWhileIdle表示空闲时检测。driverClassName要跟实际驱动 jar 匹配旧 MySQL 驱动常用com.mysql.jdbc.Driver新驱动类名不同别照抄。密码明文放在配置文件里是旧环境的现实至少把文件权限收紧用独立数据库账号不要给 DBA 权限。应用侧在web.xml里声明资源引用!-- WEB-INF/web.xml -- resource-ref descriptiondemo datasource/description res-ref-namejdbc/demo/res-ref-name res-typejavax.sql.DataSource/res-type res-authContainer/res-auth /resource-refJava 代码里这样查找// 旧项目常见 JNDI 查找方式 Context initCtx new InitialContext(); DataSource ds (DataSource) initCtx.lookup(java:comp/env/jdbc/demo); Connection conn ds.getConnection();java:comp/env/是固定前缀后面跟web.xml里的res-ref-name。查找失败时先看catalina.out里有没有NameNotFoundException再看驱动 jar 是否在lib最后确认 Context 文件是否被加载。连接池参数不是越大越好maxActive50而数据库只允许 30 个连接压测时会出现获取连接超时表面看是 Tomcat 卡住实际是数据库连接被打满。5. 避坑与排查启动闪退、乱码、类加载泄漏的现场记录Tomcat 7 的很多问题不是版本 bug而是环境、路径、配置残留叠在一起。下面几条是旧系统维护里反复出现的现场按“现象 → 原因 → 解决”记录能少走一些弯路。5.1 启动阶段的三类翻车JAVA_HOME、端口、XML 语法现象一Windows 下双击startup.bat窗口一闪就没了应用没起来。原因通常是JAVA_HOME没配、指向了 JRE 而不是 JDK或者路径里有空格导致脚本解析失败。解决方式是打开 cmd切到bin目录执行catalina.bat run让报错留在窗口里再执行echo %JAVA_HOME%和java -version确认。Linux 下对应看catalina.sh run输出报Neither the JAVA_HOME nor the JRE_HOME environment variable is defined就是同一类问题。现象二日志里出现java.net.BindException: Address already in use: JVM_Bind8080 起不来。原因是 8080 被其他进程占用常见是上一个 Tomcat 没退干净或者本机装了其他 Web 服务。Windows 下用netstat -ano | findstr :8080找 PID再在任务管理器里核对Linux 下用ss -lntp | grep :8080。确认是旧 Tomcat 就正常关闭确认是别的服务就改server.xml的port同时改前端代理和防火墙规则。不要直接杀不认识的进程先看命令行。# Linux 查看 8080 占用 ss -lntp | grep :8080 # Windows 查看 8080 占用 netstat -ano | findstr :8080 # 改配置前先做语法检查 cd /opt/apps/tomcat-7.0.108/bin ./catalina.sh configtestss和netstat只负责找占用者不负责判断能不能杀。configtest只检查server.xml基本结构不检查端口是否被占所以启动失败时两个都要看。改完端口后redirectPort和shutdown端口也要确认没有冲突。现象三改完server.xml后 Tomcat 直接起不来日志只有 XML 解析错误。原因是标签没闭合、属性引号少了、注释里写了--或者复制配置时把没转义。解决方式是先恢复备份再用configtest定位行号。XML 里的要写成amp;在属性里要转义。旧机器没有版本管理时改之前先cp server.xml server.xml.bak这一步是后悔药别省。5.2 运行阶段的两类暗坑编码链路与 reloadable 类加载泄漏现象四GET 请求中文参数乱码POST 表单正常。原因是 Connector 的URIEncoding没设或设错GET 查询串按默认字符集解码POST 体由request.setCharacterEncoding控制所以看起来“POST 正常”。解决方式是 Connector 加URIEncodingUTF-8需要让查询串跟随请求体编码时加useBodyEncodingForURItrue重启后验证。若仍乱码检查前端是否对参数做了两次encodeURIComponent或者网关是否重写了查询串。编码问题要顺着“浏览器 → 代理 → Tomcat → 过滤器 → 数据库”一路看别只在 JSP 里加charset。现象五开发期开了reloadabletrue反复热部署后出现OutOfMemoryError、ClassNotFoundException或应用越跑越慢。原因是 Tomcat 热加载会创建新的类加载器旧类加载器如果被线程、JDBC 驱动、日志框架、定时任务持有引用就回收不掉JDK 7 下表现为 PermGen 压力JDK 8 下表现为 Metaspace 增长。解决方式是把生产环境reloadable设为false升级走重启排查时看logs/catalina.out和 GC 日志确认是老应用类泄漏而不是单纯加大内存。JDK 7 下可临时调-XX:MaxPermSize但这只是缓解不是修复。清work目录只能清 JSP 编译产物清不掉已经泄漏的类加载器。提示旧系统上“重启能好”的问题不要只写一个重启脚本就结束。至少记录一次重启前的线程数、堆使用、catalina.out里的异常否则下次还会在同一处翻车。6. 交付前验证三条命令确认 Tomcat 7.0.108 真能跑把 Tomcat 7.0.108 交给别人之前我会做一轮最小验证不依赖浏览器点点点。先跑配置检查再用curl看 HTTP 状态码最后扫日志里的严重错误。三条命令不复杂但能拦住“本机能开、换机就挂”的大部分问题。6.1 三条验证命令cd /opt/apps/tomcat-7.0.108/bin # 1. 配置语法检查失败会直接给行号 ./catalina.sh configtest # 2. 看根路径和业务路径的 HTTP 状态码 curl -s -o /dev/null -w %{http_code}\n http://127.0.0.1:8080/ curl -s -o /dev/null -w %{http_code}\n http://127.0.0.1:8080/demo/ # 3. 扫启动日志中的严重错误 grep -E SEVERE|Exception|ERROR logs/catalina.out | tail -20configtest返回OK只代表server.xml能解析不代表应用能访问。curl的-o /dev/null丢弃响应体-w %{http_code}\n只打印状态码根路径 200 或 404 都正常业务路径 200 才说明应用起来了。grep只看最近 20 行避免旧日志干扰。若业务路径返回 503先看应用是否部署成功返回 500直接看localhost.*.log里的堆栈。验证项通过标准失败先看configtest输出OKserver.xml行号、标签闭合根路径200 或 404Connector 端口、防火墙业务路径200webapps部署日志、Context 文件日志扫描无新增SEVEREcatalina.out末尾、应用日志6.2 我的固化习惯从那以后我每次解压 tomcat-7.0.108.zip 都强制走一遍独立目录、固定JAVA_HOME、configtest通过后再改业务配置最后用curl和日志扫描确认不靠“浏览器能打开”判断交付完成。旧环境里最贵的不是配置本身而是配置漂移把这几步写进启动脚本旁边的README换人接手也能照着复现。希望帮到你。本文还有配套的精品资源点击获取