简介:Apache Tomcat 7.0.108 是一款开源的 Java Servlet 容器,主要用于运行 JSP 与 Servlet 等 Java Web 应用。这份 zip 压缩包面向 Java Web 开发者、运维人员及计算机专业学生,帮助其快速搭建本地或测试环境的 Web 服务器,完成项目部署与调试。包内共 640 个文件,约 10.38MB,以 html 文档、class 字节码、java 源码、jsp 页面、gif 图片、jar 依赖包、xml 配置、properties 属性文件及 bat/sh 启停脚本为主,完整覆盖 bin、conf、lib、webapps 等核心目录,便于理解 Tomcat 的目录结构与运行机制。资源还包含 war 包、tld 标签库、xsd 与 dtd 约束文件等,可辅助读者掌握 WAR 部署、Context 配置、日志查看与管理工具的使用。目前已有 633 人学习下载,适合需要熟悉 Tomcat 7 部署流程、排查启动与访问问题的开发者参考。
1. 从一台老项目的部署说起:为什么我还在装 Tomcat 7.0.108
上周帮一个朋友处理他公司遗留系统的部署问题,打开pom.xml一看,javax.servlet版本还停留在 3.0,JDK 编译级别是 1.7。这种项目你上 Tomcat 9 或者 10,启动直接给你抛NoClassDefFoundError或者ClassNotFoundException,连日志都来不及打全就挂了。翻了一圈,最稳的落点就是 Tomcat 7.0.108——这是 7.x 分支最后一个发布版本,修掉了之前几个安全漏洞,又没引入 8.x 之后那些对老web.xml解析更严格的校验逻辑。
所以这篇笔记不是讲 Tomcat 怎么用,而是讲怎么把tomcat-7.0.108.zip这个包在一个干净环境里装起来、配好、跑通,顺便把我在这个版本上踩过的几个坑摊开说。适合谁看?手上维护着 JDK 7 或 JDK 8 早期版本、Servlet 3.0 规范、又不想动代码去适配新容器的开发者。如果你是新项目,直接上 Tomcat 9 或 10,别回头折腾这个版本。
2. 装之前先把环境对清楚:JDK 版本和目录规划
2.1 Tomcat 7.0.108 对 JDK 的硬性要求
Tomcat 7 整个系列编译时用的 JDK 是 1.6,运行时最低要求 JRE 1.6。但 7.0.108 这个版本我实测下来,跑在 JDK 1.7 和 JDK 1.8 上都没问题,JDK 1.6 反而因为一些加密套件的问题在 HTTPS 场景下会报SSLHandshakeException。所以我的建议很直接:用 JDK 1.8,别用 1.6,也别用 1.9 以上。
为什么不用 JDK 9+?Tomcat 7 的启动脚本catalina.sh里有一堆对JAVA_OPTS和CATALINA_OPTS的解析逻辑,在 JDK 9 的模块化系统下会触发Unrecognized VM option之类的报错。而且 JDK 11 之后移除了tools.jar,Tomcat 7 的 JSP 编译依赖它,你会在访问 JSP 页面时看到Unable to find a javac compiler。
先确认环境:
# 查看当前 JDK 版本 java -version # 输出示例: # java version "1.8.0_202" # Java(TM) SE Runtime Environment (build 1.8.0_202-b08) # Java HotSpot(TM) 64-Bit Server VM (build 25.202-b08, mixed mode) # 确认 JAVA_HOME 是否设置 echo $JAVA_HOME # 如果为空,需要手动指定,比如: export JAVA_HOME=/usr/lib/jvm/java-8-openjdk-amd64JAVA_HOME这个变量 Tomcat 的启动脚本会读,如果没设或者指向了 JRE 而不是 JDK,JSP 编译功能直接废掉。我一般会在/etc/profile.d/下单独写一个tomcat-env.sh,把JAVA_HOME和CATALINA_HOME都固化进去,避免每次重启机器都要手动 export。
2.2 目录结构怎么摆才不给自己挖坑
tomcat-7.0.108.zip解压出来就是一个apache-tomcat-7.0.108目录,里面bin、conf、webapps、logs、temp、work六个核心目录。我见过太多人直接解压到/root/或者/home/xxx/下载/下面,然后跑起来发现日志写不进去、临时文件目录权限不对。
我的习惯是统一放在/opt/tomcat/下面,用软链接指向具体版本:
# 创建统一目录 sudo mkdir -p /opt/tomcat # 解压 zip 包到指定目录 sudo unzip tomcat-7.0.108.zip -d /opt/tomcat/ # 建立软链接,方便后续版本切换 sudo ln -s /opt/tomcat/apache-tomcat-7.0.108 /opt/tomcat/current # 确认目录结构 ls -l /opt/tomcat/current/ # 应该看到 bin conf lib logs temp webapps work 等目录用软链接的好处是:以后升级版本,只需要把新版本解压到/opt/tomcat/下,然后把current指向新目录,CATALINA_HOME环境变量不用改。另外logs和temp目录建议单独挂载或者至少确保运行 Tomcat 的用户有写权限,否则启动时会报java.io.FileNotFoundException: /opt/tomcat/current/logs/catalina.out (Permission denied)。
提示:如果你打算用 80 端口跑 Tomcat,Linux 下非 root 用户无法绑定 1024 以下端口。要么用
authbind授权,要么在前面挂 Nginx 做反向代理。我一般选后者,Tomcat 只监听 8080,Nginx 转发 80 和 443。
3. 解压、配置、启动:把 zip 包变成能跑的服务
3.1 解压后的第一轮配置:server.xml 改哪几个地方
conf/server.xml是 Tomcat 的核心配置文件,7.0.108 这个版本的默认配置里,有几个地方我每次都会改。
第一个是Connector的URIEncoding。默认没写,在中文环境下 GET 请求参数会乱码。改成 UTF-8:
<!-- conf/server.xml 中找到 Connector 标签 --> <Connector port="8080" protocol="HTTP/1.1" connectionTimeout="20000" redirectPort="8443" URIEncoding="UTF-8" maxThreads="300" minSpareThreads="30" acceptCount="500" />URIEncoding="UTF-8"解决 GET 请求中文乱码;maxThreads默认是 200,我调到 300 是因为老项目里有些同步阻塞的 Servlet,线程池太小会排队;acceptCount是等待队列长度,默认 100,调到 500 防止高并发时直接拒绝连接。
第二个是Host标签里的appBase和unpackWARs。默认appBase="webapps",unpackWARs="true",这个组合没问题。但如果你把 WAR 包放在外部目录,需要加一个Context标签指定docBase。我一般不动这个,直接把 WAR 丢进webapps让它自动解压。
第三个是AccessLogValve,默认是注释掉的。生产环境建议打开,方便排查请求问题:
<!-- 在 Host 标签内取消注释并修改 --> <Valve className="org.apache.catalina.valves.AccessLogValve" directory="logs" prefix="localhost_access_log." suffix=".txt" pattern="%h %l %u %t "%r" %s %b %D" />%D是请求处理时间,单位毫秒,排查慢请求时很有用。默认的pattern里没有这个,我习惯加上。
3.2 启动脚本和 JVM 参数怎么调
Tomcat 7 的启动脚本在bin/catalina.sh,但我不建议直接改这个文件,因为版本升级时会被覆盖。正确做法是在bin/下新建一个setenv.sh,Tomcat 启动时会自动读取:
#!/bin/bash # /opt/tomcat/current/bin/setenv.sh # JVM 内存参数 export CATALINA_OPTS="$CATALINA_OPTS -Xms512m -Xmx1024m" export CATALINA_OPTS="$CATALINA_OPTS -XX:PermSize=128m -XX:MaxPermSize=256m" # 时区和编码 export CATALINA_OPTS="$CATALINA_OPTS -Duser.timezone=Asia/Shanghai" export CATALINA_OPTS="$CATALINA_OPTS -Dfile.encoding=UTF-8" # 关闭 DNS 查询,加快启动速度 export CATALINA_OPTS="$CATALINA_OPTS -Djava.net.preferIPv4Stack=true"注意PermSize和MaxPermSize这两个参数在 JDK 8 里已经被MetaspaceSize和MaxMetaspaceSize取代了。如果你用 JDK 8,会看到Java HotSpot(TM) 64-Bit Server VM warning: ignoring option PermSize=128m; support was removed in 8.0的警告,不影响运行,但看着烦。换成:
# JDK 8 环境下用这两个 export CATALINA_OPTS="$CATALINA_OPTS -XX:MetaspaceSize=128m -XX:MaxMetaspaceSize=256m"-Xms和-Xmx设成一样大可以避免堆动态扩容带来的性能抖动,我一般设成物理内存的一半,但不超过 2G,因为 Tomcat 7 在老项目里通常不会跑太大的堆。
给setenv.sh加执行权限:
chmod +x /opt/tomcat/current/bin/setenv.sh然后启动:
# 启动 Tomcat /opt/tomcat/current/bin/startup.sh # 查看启动日志 tail -f /opt/tomcat/current/logs/catalina.out看到Server startup in xxx ms就说明启动成功了。如果卡在Starting Servlet Engine不动,大概率是某个 webapp 的web.xml解析出问题,去logs/localhost.log里找具体是哪个应用。
3.3 把 Tomcat 注册成 systemd 服务
用startup.sh启动的 Tomcat 在终端关闭后可能会挂掉,而且开机不会自启。生产环境我一般注册成 systemd 服务:
# /etc/systemd/system/tomcat.service [Unit] Description=Apache Tomcat 7.0.108 After=network.target [Service] Type=forking User=tomcat Group=tomcat Environment=JAVA_HOME=/usr/lib/jvm/java-8-openjdk-amd64 Environment=CATALINA_HOME=/opt/tomcat/current Environment=CATALINA_BASE=/opt/tomcat/current ExecStart=/opt/tomcat/current/bin/startup.sh ExecStop=/opt/tomcat/current/bin/shutdown.sh Restart=on-failure RestartSec=10 [Install] WantedBy=multi-user.target这里有几个点要注意:Type=forking是因为startup.sh会 fork 一个后台进程;User和Group要提前创建,并且把/opt/tomcat/目录的属主改成这个用户;Restart=on-failure让 Tomcat 崩溃后自动拉起,但如果是配置错误导致启动失败,会无限重启,所以调试阶段建议先注释掉这行。
# 创建 tomcat 用户 sudo useradd -r -s /sbin/nologin tomcat sudo chown -R tomcat:tomcat /opt/tomcat/ # 重载 systemd 并启动 sudo systemctl daemon-reload sudo systemctl start tomcat sudo systemctl enable tomcat # 查看状态 sudo systemctl status tomcat4. 避坑记录:Tomcat 7.0.108 上我踩过的五个坑
4.1 坑一:JSP 页面报 "Unable to find a javac compiler"
现象:访问 JSP 页面时返回 500,日志里写org.apache.jasper.JasperException: Unable to find a javac compiler。
原因:Tomcat 7 的 JSP 编译依赖 JDK 的tools.jar,如果JAVA_HOME指向的是 JRE 而不是 JDK,或者 JDK 9 以上版本移除了tools.jar,就会报这个错。
解决:确认JAVA_HOME指向 JDK 安装目录,且目录下有lib/tools.jar。JDK 8 的路径一般是/usr/lib/jvm/java-8-openjdk-amd64,检查ls $JAVA_HOME/lib/tools.jar是否存在。如果用的是 JDK 9+,要么降级到 JDK 8,要么在setenv.sh里加-Djava.endorsed.dirs指向兼容包,但后者很麻烦,不推荐。
4.2 坑二:启动时报 "Address already in use"
现象:startup.sh执行后日志显示java.net.BindException: Address already in use,端口被占用。
原因:8080 端口被其他进程占了,可能是之前没关干净的 Tomcat 进程,也可能是其他 Web 服务。
解决:
# 查看 8080 端口占用情况 sudo lsof -i :8080 # 或者 sudo netstat -tlnp | grep 8080 # 如果确认是残留的 Tomcat 进程,直接 kill sudo kill -9 <PID>如果不想杀进程,改server.xml里的Connector port换个端口,比如 8081。但注意redirectPort也要同步改,否则 HTTPS 跳转会出问题。
4.3 坑三:中文参数乱码,POST 和 GET 表现不一致
现象:POST 请求中文正常,GET 请求中文乱码,或者反过来。
原因:Tomcat 7 默认的URIEncoding是 ISO-8859-1,GET 请求参数按这个编码解析。POST 请求的编码由web.xml里的CharacterEncodingFilter或者请求头Content-Type决定。
解决:在server.xml的Connector里加URIEncoding="UTF-8",同时确保应用的web.xml里配了CharacterEncodingFilter:
<filter> <filter-name>encodingFilter</filter-name> <filter-class>org.springframework.web.filter.CharacterEncodingFilter</filter-class> <init-param> <param-name>encoding</param-name> <param-value>UTF-8</param-value> </init-param> <init-param> <param-name>forceEncoding</param-name> <param-value>true</param-value> </init-param> </filter> <filter-mapping> <filter-name>encodingFilter</filter-name> <url-pattern>/*</url-pattern> </filter-mapping>两边都配了才能保证 GET 和 POST 都不乱码。
4.4 坑四:WAR 包自动解压后,旧文件没被覆盖
现象:更新 WAR 包后重启 Tomcat,发现代码还是旧的。
原因:Tomcat 默认的unpackWARs="true"会把 WAR 解压到webapps/下同名目录,但如果旧目录里的文件时间戳比 WAR 包里的新,Tomcat 不会覆盖。
解决:每次更新 WAR 包之前,先删掉旧的解压目录和 WAR 文件:
# 停止 Tomcat /opt/tomcat/current/bin/shutdown.sh # 删除旧的应用目录和 WAR 包 rm -rf /opt/tomcat/current/webapps/yourapp rm -f /opt/tomcat/current/webapps/yourapp.war # 拷贝新 WAR 包 cp yourapp.war /opt/tomcat/current/webapps/ # 启动 /opt/tomcat/current/bin/startup.sh或者把unpackWARs改成false,让 Tomcat 直接从 WAR 包运行,但这样 JSP 编译会变慢,而且有些框架读取WEB-INF下资源的方式会出问题。我一般还是用true,手动清理。
4.5 坑五:日志文件 catalina.out 无限增长
现象:磁盘空间被catalina.out占满,Tomcat 写不进日志后行为异常。
原因:catalina.out是 Tomcat 的标准输出重定向文件,不会自动切割。7.0.108 的logging.properties里配置的是java.util.logging,但catalina.out不受它管理。
解决:用logrotate做切割:
# /etc/logrotate.d/tomcat /opt/tomcat/current/logs/catalina.out { daily rotate 7 missingok compress copytruncate notifempty }copytruncate是关键,它先复制文件再清空原文件,不需要重启 Tomcat。rotate 7保留 7 天。配好之后手动跑一次logrotate -f /etc/logrotate.d/tomcat验证。
5. 装完怎么验证:从端口到 JSP 到 WAR 部署的完整检查链
装完 Tomcat 之后,别急着丢 WAR 包进去,先按这个顺序走一遍验证,能省掉后面很多排查时间。
第一步:检查进程和端口
# 确认 Tomcat 进程存在 ps -ef | grep tomcat | grep -v grep # 确认 8080 端口监听 ss -tlnp | grep 8080如果进程在但端口没监听,说明Connector配置有问题,去catalina.out里搜BindException或LifecycleException。
第二步:访问默认首页
curl -I http://localhost:8080/ # 应该返回 HTTP/1.1 200 OK如果返回 404,检查webapps/ROOT目录是否存在。7.0.108 的 zip 包里自带ROOT应用,如果被误删了,可以从conf/Catalina/localhost/下看看有没有残留配置。
第三步:验证 JSP 编译
在webapps/ROOT/下建一个test.jsp:
<%@ page contentType="text/html;charset=UTF-8" language="java" %> <html> <head><title>环境验证</title></head> <body> <% out.println("JSP 编译正常,当前时间:" + new java.util.Date()); out.println("JAVA_HOME:" + System.getProperty("java.home")); %> </body> </html>访问http://localhost:8080/test.jsp,如果能看到时间输出,说明 JSP 编译链路通了。如果报 500,回到 4.1 节检查tools.jar。
第四步:部署一个测试 WAR 包
拿一个最简单的 WAR 包丢进webapps/,观察日志:
# 实时监控日志 tail -f /opt/tomcat/current/logs/catalina.out # 看到类似输出说明部署成功: # Deployment of web application archive /opt/tomcat/current/webapps/test.war has finished in 1,234 ms然后访问http://localhost:8080/test/确认应用响应正常。
第五步:检查日志目录权限和磁盘空间
# 确认 logs 目录可写 touch /opt/tomcat/current/logs/test.log && rm /opt/tomcat/current/logs/test.log # 检查磁盘使用率 df -h /opt/tomcat/我一般会在部署完成后跑一个简单的压力测试,用ab或者wrk打 1000 个请求,看线程池和内存有没有异常:
# 安装 apache2-utils 后使用 ab ab -n 1000 -c 50 http://localhost:8080/关注Failed requests和Time per request两个指标。如果失败率超过 1%,去logs/localhost_access_log里看具体是哪些请求挂了。
从那以后我每次装完 Tomcat,不管多急,都会把这五步走一遍。尤其是 JSP 编译那步,很多问题在部署 WAR 之前就能暴露出来,比等到应用上线后才发现要省事得多。希望帮到你。
本文还有配套的精品资源,点击获取