Tomcat 7.0.108 旧系统部署与排错实战

发布时间:2026/10/26 9:40:08
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 7:tomcat-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 本身坏了,而是解压到了带空格、带中文的路径,或者机器上装了三个 JDK,JAVA_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 LANG=zh_CN.UTF-8 export LC_ALL=zh_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_HOME=C:\java\jdk8" set "CATALINA_HOME=D:\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.encoding=UTF-8"

setenv.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 run

configtest会先检查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 默认的protocol="HTTP/1.1"在很多包中走 BIO,连接数一高就容易被阻塞。旧机器如果 JDK 支持,可以换成 NIO;但要确认项目里没有依赖 BIO 的特殊行为。线程数不是越大越好,maxThreads调到 500 而数据库连接池只有 20,结果只是把压力堆在 Tomcat 里。下面是一段可落地的Connector配置:

<!-- conf/server.xml 片段,改前先备份 --> <Server port="8005" shutdown="SHUTDOWN"> <Service name="Catalina"> <Connector port="8080" protocol="org.apache.coyote.http11.Http11NioProtocol" URIEncoding="UTF-8" useBodyEncodingForURI="true" maxThreads="200" minSpareThreads="20" acceptCount="100" maxConnections="5000" connectionTimeout="20000" compression="on" compressableMimeType="text/html,text/xml,text/plain,text/css,application/javascript,application/json" redirectPort="8443" /> <Engine name="Catalina" defaultHost="localhost"> <Host name="localhost" appBase="webapps" unpackWARs="true" autoDeploy="true"> <Valve className="org.apache.catalina.valves.AccessLogValve" directory="logs" prefix="access." suffix=".log" pattern="%h %l %u %t &quot;%r&quot; %s %b %D" /> </Host> </Engine> </Service> </Server>

port是 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 里,URIEncoding="UTF-8"决定 URI 路径和查询串按什么字符集解码;useBodyEncodingForURI="true"表示 URI 查询串使用请求体的编码,而不是固定用URIEncoding。这两个参数不是互相替代,配错会出现“浏览器地址栏正常,日志里乱码”的情况。旧项目里还常见request.setCharacterEncoding("UTF-8")只对 POST 体生效,GET 查询串仍走 Connector 配置。

我一般会同时做三件事:Connector 设URIEncoding="UTF-8",过滤器里对 POST 设 UTF-8,JSP 页面声明pageEncoding="UTF-8"。如果应用里有过滤器,注意过滤器顺序,编码过滤器要排在参数读取之前。还有一种情况是前端用application/x-www-form-urlencoded提交,后端却按multipart/form-data解析,参数根本进不到request.getParameter,这时候不是编码问题,是 Content-Type 不匹配,别在server.xml里反复改。

注意:改完URIEncoding必须重启 Tomcat,热加载不会让 Connector 重新初始化。旧连接池里的连接不受影响,但新请求才会走新编码。

3.3 Host 与 Valve:访问日志和远程地址拿到真实值

Host里的appBase="webapps"决定默认部署目录,unpackWARs="true"表示 war 包会自动解压,autoDeploy="true"表示运行期检测新 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和资源。reloadable="true"方便开发期改类后自动重载,但生产环境不建议开,类加载器泄漏和内存增长很难查。crossContext="false"是默认值,多个应用之间不要随便共享 ServletContext。下面是一个外部目录部署的例子:

<!-- conf/Catalina/localhost/demo.xml --> <Context docBase="/data/apps/demo" reloadable="false" crossContext="false"> <!-- 会话超时按分钟计,旧系统常用 30 --> <SessionCookie path="/demo" /> </Context>

docBase可以指向解压后的应用目录,也可以指向 war 文件。指向目录时,升级只需替换目录内容;指向 war 时,Tomcat 会按unpackWARs决定是否解压。reloadable="false"表示不监控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" reloadable="false"> <Resource name="jdbc/demo" auth="Container" type="javax.sql.DataSource" driverClassName="com.mysql.jdbc.Driver" url="jdbc:mysql://127.0.0.1:3306/demo?useUnicode=true&amp;characterEncoding=UTF-8" username="demo_app" password="replace_me" maxActive="50" maxIdle="10" minIdle="2" maxWait="10000" validationQuery="SELECT 1" testWhileIdle="true" timeBetweenEvictionRunsMillis="30000" /> </Context>

maxActive是最大活动连接数,要和数据库端max_connections一起看;maxIdle是最大空闲连接,minIdle是最小空闲连接;maxWait是获取连接的最长等待毫秒数;validationQuery用来检测连接是否可用,testWhileIdle表示空闲时检测。driverClassName要跟实际驱动 jar 匹配,旧 MySQL 驱动常用com.mysql.jdbc.Driver,新驱动类名不同,别照抄。密码明文放在配置文件里是旧环境的现实,至少把文件权限收紧,用独立数据库账号,不要给 DBA 权限。

应用侧在web.xml里声明资源引用:

<!-- WEB-INF/web.xml --> <resource-ref> <description>demo datasource</description> <res-ref-name>jdbc/demo</res-ref-name> <res-type>javax.sql.DataSource</res-type> <res-auth>Container</res-auth> </resource-ref>

Java 代码里这样查找:

// 旧项目常见 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 文件是否被加载。连接池参数不是越大越好,maxActive="50"而数据库只允许 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_Bind,8080 起不来。
原因是 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 configtest

ss和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 加URIEncoding="UTF-8",需要让查询串跟随请求体编码时加useBodyEncodingForURI="true",重启后验证。若仍乱码,检查前端是否对参数做了两次encodeURIComponent,或者网关是否重写了查询串。编码问题要顺着“浏览器 → 代理 → Tomcat → 过滤器 → 数据库”一路看,别只在 JSP 里加charset。

现象五:开发期开了reloadable="true",反复热部署后出现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 -20

configtest返回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,换人接手也能照着复现。希望帮到你。

本文还有配套的精品资源,点击获取