
简介apache-tomcat-8.5.38-windows-x64.zip 是 Apache Tomcat 8.5.38 的 Windows 64 位免安装压缩包面向需要在本地搭建 Java Web 运行环境、学习 Servlet 与 JSP 开发的初学者及 J2EE 应用开发者。它提供轻量级 Servlet/JSP 容器解压后通过 bin 目录下的启停脚本即可运行适用于课程实验、项目调试与小型应用部署等场景。压缩包共 645 个文件整体约 11.08MB既包含 Manager 管理界面相关的 html、xhtml 页面也含 Tomcat 自身实现与示例的 class、java、jsp 源码jar、properties、xml 提供依赖与配置bat、sh 为启停脚本gif、png、svg 等则是界面静态资源共同构成一套可直接运行的完整服务器目录。已有 140 人学习下载使用者可借此熟悉 conf 下 server.xml、web.xml、tomcat-users.xml 的配置方式掌握 war 包部署、管理界面使用、SSL 连接器调整与日志排查等实践为在 IDE 中集成 Tomcat、理解 J2EE 容器机制打下基础。1. 一个 8.5.38 的 Windows 包为什么还是很多人接触 J2EE 的第一站接手一个跑了七八年的 J2EE 老系统做本地排查对方发过来的往往不是源码仓库而是一个 apache-tomcat-8.5.38-windows-x64.zip附一句「你先在本地跑起来看看」。解压、配 JDK、双击启动三步之内就能判断这台机器到底有没有条件接手这套东西。它实现的是 Servlet 3.1、JSP 2.3 这一代规范属于容器层面的「最小可运行单元」不带任何业务代码但把请求分发、会话、类加载、部署目录这些机制全都摆在你面前。适合两类人一类是要在 Windows 上快速搭出调试环境、不想折腾构建工具的后端同学另一类是接手遗留系统、必须搞清楚「war 放进去之后到底发生了什么」的维护者。这份包的价值不在新而在可预期——目录结构、配置文件、日志格式十年不变出了问题有据可查。2. 解压之后先别急着启动conf 和 bin 两个目录决定了它能跑成什么样拿到压缩包的第一反应通常是找 startup.bat但从排错的角度看先花十分钟把目录结构过一遍后面能省掉大量「启动黑窗口一闪而过」的时间。Windows 版的分发包是一个完整的 CATALINA_HOME容器本身、配置、示例应用、日志目录全在里面改错一个文件就可能起不来而所有需要动的地方其实集中在 conf 和 bin。2.1 这份分发包里哪些目录真的会被改到解压出来大致是这样一棵树先把每一层的作用记清楚比看任何文档都快目录主要内容是否需要改动说明binstartup.bat、shutdown.bat、catalina.bat、setclasspath.bat、service.bat、tomcat8.exe会启动脚本与服务包装器通常只新增 setenv.batconfserver.xml、web.xml、context.xml、tomcat-users.xml、logging.properties、catalina.properties会端口、虚拟主机、编码、日志级别都在这里libbootstrap.jar、catalina.jar、tomcat-coyote.jar、servlet-api.jar、jsp-api.jar、el-api.jar、tomcat-juli.jar基本不动容器级公共类库应用打包时不要重复带入webappsROOT、docs、examples、host-manager、manager会应用部署目录示例应用可以整目录删掉logscatalina.YYYY-MM-DD.log、localhost.YYYY-MM-DD.log、localhost_access_log.YYYY-MM-DD.txt只读排错第一现场workJSP 编译出来的 .java 与 .class偶尔删编译缓存清空后重启会重新生成temp临时文件不动JVM 与容器共用有两个细节值得单独说。第一lib 下的 servlet-api.jar、jsp-api.jar 是容器提供给应用的接口包打 war 的时候千万不要把同名 jar 一起打进去否则会出现两个同名类被不同类加载器加载报ClassCastException或者过滤器莫名不生效这类问题查起来非常费时间。第二work 目录是 JSP 的编译产物缓存改了 JSP 但页面行为没变、或者报编译错误却看不到新日志时直接把对应应用子目录删掉重启多数情况能立刻恢复正常。我一般会在启动脚本旁边放一个清缓存的批处理排查前端页面类问题时先用一次。2.2 端口、JVM 参数、编码解压后最先要动的三处conf/server.xml 里默认监听两个端口Server port8005 shutdownSHUTDOWN是关闭指令端口HTTP 连接器是 8080。同一台机器上要跑多个实例或者 8080 已经被别的服务占了就要从这里改。注意 8005 不改而只改 8080两个实例同时跑时会因为关闭端口冲突导致后启动的那个关不掉只能去任务管理器里结束进程。!-- conf/server.xml 片段只改必要项其余保持默认 -- Server port8005 shutdownSHUTDOWN Service nameCatalina Connector port8080 protocolHTTP/1.1 connectionTimeout20000 redirectPort8443 URIEncodingUTF-8 maxThreads200 acceptCount100 / Engine nameCatalina defaultHostlocalhost Host namelocalhost appBasewebapps unpackWARstrue autoDeploytrue /Host /Engine /Service /Server参数含义按改动的优先级说port是监听端口protocolHTTP/1.1在 8.5 这一代默认走 NIO 实现不用显式写Http11NioProtocolconnectionTimeout是连接建立后等待请求行的毫秒数默认值 20000 够用压测时不要为了「响应快」把它调到几百毫秒那只会把慢客户端直接掐断maxThreads与acceptCount是并发处理的核心前者是工作线程上限后者是等待队列长度队列满了之后的连接会被直接拒绝这两个值的关系在第 4 章展开URIEncodingUTF-8在 8.x 的 HTTP 连接器上默认已经是 UTF-8显式写出来主要是防后续有人改回旧值属于「写死比默认值可信」的那一类配置。JVM 参数不要直接改 catalina.bat那是升级或替换版本时一定会被覆盖的文件。正确做法是在 bin 下新建 setenv.bat启动脚本会自动调用它echo off rem bin/setenv.bat —— 只在当前实例生效不污染系统环境变量 set CATALINA_HOMED:\server\apache-tomcat-8.5.38 set CATALINA_BASED:\server\apache-tomcat-8.5.38 set JAVA_HOMEC:\Java\jdk1.8.0 set CATALINA_OPTS-Xms512m -Xmx1024m -XX:MaxMetaspaceSize256m -Dfile.encodingUTF-8 -Duser.timezoneGMT08 set JAVA_OPTS%JAVA_OPTS% -Djava.awt.headlesstrue这里有两个容易混的变量。CATALINA_OPTS只在启动容器时传给 JVM适合放堆大小、时区、文件编码这类运行期参数JAVA_OPTS会被 shutdown、version 等命令一并带上放内存参数会导致关闭脚本也去申请同样大小的堆机器内存紧张时关闭动作会变慢甚至失败。我的习惯是内存参数一律进 CATALINA_OPTS只有确实需要所有命令共享的属性才写 JAVA_OPTS。-Dfile.encodingUTF-8在 Windows 中文环境下尤其重要控制台默认代码页是 GBK日志里的中文如果出现乱码第一嫌疑就是它没设。-XX:MaxMetaspaceSize是给类元数据兜底的热部署频繁的调试环境不设上限几天后可能把机器吃满。2.3 解压路径的三条硬约束踩过就知道疼路径这件事看起来无关技术实际上是最常见的一类「启动失败」。第一条不要解压到 Program Files 或 Program Files (x86) 下面。这些目录默认需要管理员权限写入而 Tomcat 启动时要在 logs 下创建当天的日志文件、在 work 下写编译产物权限不足时的报错往往藏在日志深处表现却是「窗口一闪就没了」。放在 D:\server 这类普通目录下最省心。第二条路径里不要有中文和空格。catalina.bat 内部对路径做了引号处理大部分情况没问题但一些第三方脚本、服务安装工具、以及从别的机器拷过来的启动脚本对空格和中文的处理并不可靠。第二条的变体是路径别太深Windows 的路径长度限制还在war 包解压到多层级目录时超过长度会抛The filename or extension is too long这个报错跟 Tomcat 本身没关系但报在部署阶段很容易被误判成包坏了。第三条如果要在同一台机器上跑多个实例用 CATALINA_HOME 与 CATALINA_BASE 分离的方式把解压目录当作 HOME 只读共享每个实例建一个自己的 BASE里面放 conf、logs、webapps、work、temp 五个目录启动前设好这两个变量。这样升级容器版本时只换 HOME各实例的配置和部署物不受影响。单实例调试没必要这么麻烦但一旦开始做多环境验证这个习惯能省掉大量复制粘贴。3. 从零跑起第一个 J2EE 应用JDK 匹配、启动方式与部署路径环境配好之后真正的动作只有三个确认 JDK 版本对得上、用能看到报错的方式启动、把应用放到容器认识的位置。这三步里任何一步用「双击试试」的方式糊过去后面出问题都只能靠猜。3.1 JDK 版本与 class 文件版本先对齐再谈部署8.5 这一代容器要求 Java 7 及以上跑在 Java 8 上是最常见的组合。但「装了 JDK」不等于「Tomcat 用对了 JDK」机器上装了三四个版本、PATH 里的是 11 而项目是 8 编译的这种情况非常普遍。先验证rem 在 cmd 中执行确认当前生效的 JDK 版本 java -version javac -version rem 查看 Tomcat 实际使用的 JAVA_HOME echo %JAVA_HOME%输出里java version 1.8.0_xxx说明运行时是 8。如果编译用的是更高版本部署后会直接抛出java.lang.UnsupportedClassVersionError报错信息里会带两个数字比如compiled by a more recent version of the Java Runtime (class file version 55.0), this version only recognizes ... 52.0。这两个数字就是 class 文件版本号对照关系是52 对应 Java 853 对应 Java 955 对应 Java 11。看到这个错不要去改 Tomcat 配置那是编译目标版本和运行版本不一致要么换 JDK要么在构建里把 source 和 target 降到 8。如果机器上必须保留多个 JDK就在 setenv.bat 里显式写死 JAVA_HOME不要依赖系统环境变量。系统变量是全局的改一次影响所有程序而 setenv.bat 只影响这一个实例出问题范围可控。3.2 用 catalina.bat run 启动日志直接打在控制台startup.bat 会另开一个窗口出错时窗口一闪而过什么都看不到。调试阶段我一般直接用 run 模式日志全部输出到当前控制台报错第一行就能看见cd /d D:\server\apache-tomcat-8.5.38\bin catalina.bat run启动过程里值得盯住的几行日志Using CATALINA_BASE和Using CATALINA_HOME告诉你容器到底加载了哪个目录的配置多个实例环境下这是排查「改了配置不生效」的第一证据Server version name: Apache Tomcat/8.5.38确认版本没拿错Deploying web application archive [...]说明 war 被识别到了最后一行Server startup in [xxxx] milliseconds出现才代表启动完成。如果卡在某个应用部署上不动通常是那个应用的ServletContextListener里做了阻塞操作比如连数据库超时、读远程配置没设超时这时候日志会停在部署那一行需要用 jstack 看线程栈。run与start的区别只在前台还是后台配置完全一样。确认能正常启动之后再用 startup.bat 挂到后台日常开发用 run 更利于观察。停止时用catalina.bat stop或者在 run 的窗口里按 CtrlC后者更干脆但会跳过应用里的销毁回调调试阶段无所谓做优雅停机验证时要用 stop。3.3 war 包、解压目录与 context path部署路径到底怎么算把应用放进 webapps 之后访问路径由文件名决定这条规则很多人是靠试出来的。war 叫 demo.war访问路径就是/demo想挂到根路径必须叫 ROOT.war。部署有两条路rem 方式一拷贝 war 包让容器自己解压 copy /Y D:\build\demo.war D:\server\apache-tomcat-8.5.38\webapps\ rem 方式二直接放解压后的目录调试期改 JSP 更快 xcopy /E /I /Y D:\build\demo D:\server\apache-tomcat-8.5.38\webapps\demoHost 上的三个属性决定行为unpackWARstrue让容器把 war 解压成目录再加载改 JSP 能立刻生效autoDeploytrue让容器定时扫描 webapps发现新增或更新就热部署deployOnStartuptrue控制启动时是否全量扫描。开发环境三个都开着最方便但要注意热部署会重新加载整个应用静态变量全部重置会话默认也会失效调试有状态的功能时容易误判成 bug。生产环境常见做法是关掉 autoDeploy用重启或外部脚本控制发布节奏避免容器在不可预期的时刻重新加载应用。另一条容易忽略的规则war 包和同名目录同时存在时容器优先用目录war 包会被忽略。改了 war 拷进去没生效先看看旁边是不是还留着一个旧的解压目录删掉它再重启比反复检查 war 内容快得多。4. server.xml 与 web.xml把默认配置改成能扛住真实流量的样子默认配置是给开发用的能跑通不等于能用。真正上线前需要调整的地方集中在三块连接器的并发参数、虚拟主机与上下文映射、以及从请求到页面这条链路上的编码设置。三块都改错过的代价远比读一遍配置高。4.1 Connector 参数maxThreads 和 acceptCount 到底是什么关系这两个值经常被一起改但作用完全不同。maxThreads是实际处理请求的工作线程上限默认 200acceptCount是操作系统的等待队列长度线程全忙时新连接在这里排队队列满了就直接拒绝默认 100。所以单实例能同时接住的连接数大致是两者之和超出部分拿不到响应而是连接重置客户端看到的是Connection reset很容易被误判成网络问题。参数默认值作用调整建议maxThreads200并发工作线程上限按 IO 密集程度定纯查询类可到 300400CPU 密集保持 200 以内minSpareThreads10常驻空闲线程低峰期也要保持一定数量减少突发时的创建开销acceptCount100等待队列长度配合 maxThreads 一起放大不要超过 1000队列太长只会让超时更晚暴露maxConnections10000NIO 模式下可维持的连接数一般不用动短连接场景意义不大connectionTimeout20000等待请求行的毫秒数内网可降到 500010000快速释放无效连接URIEncodingUTF-8URL 解码字符集8.x 默认已是 UTF-8显式写死防回退调参的前提是有观测。没有 access log 和线程数监控的情况下加线程只是在延长排队的队伍并不能提高吞吐。我一般的顺序是先开 access log 看响应时间分布确认瓶颈在应用侧还是连接侧再决定动 maxThreads最后才考虑 acceptCount。把 maxThreads 从 200 直接改到 1000 而数据库连接池还是 20结果一定是线程全堵在拿连接上机器负载飙高、吞吐反而下降。调整后要验证的是「拒绝行为是否符合预期」而不是「能不能起来」。用压测工具把并发打到超过 maxThreads acceptCount观察日志里是否出现连接被拒、响应时间是否陡增这一步在准生产环境里做一次比上线后半夜被叫起来强。4.2 Host 与 Context路径映射到底谁说了算应用挂在哪个路径上由三层配置逐级决定。最外层是 Engine 的defaultHost指向默认虚拟主机中间是 Host 的name和appBase决定域名和部署根目录最内层是应用自己的 context path默认由 war 文件名推导。!-- conf/server.xml一个 Engine 下挂多个 Host 的写法 -- Engine nameCatalina defaultHostlocalhost Host namelocalhost appBasewebapps unpackWARstrue autoDeploytrue Valve classNameorg.apache.catalina.valves.AccessLogValve directorylogs prefixlocalhost_access_log suffix.txt pattern%h %l %u %t quot;%rquot; %s %b %D / /Host Host nameapp.internal appBasewebapps_app unpackWARstrue autoDeployfalse / /Engine这段配置里有两个关键点。第一pattern末尾的%D是处理耗时毫秒排查「哪个接口慢」时比默认格式有用得多默认的 access log Valve 在分发包里是注释状态需要手工放开。第二增加一个 Host 就要增加一个 appBase 目录两个 Host 指向同一个 appBase 会导致应用被重复加载启动日志里出现两份Deploying记录会话和静态资源都会串。有些老项目会习惯在 server.xml 里直接写Context path/xxx docBase...。这种方式在 8.5 上仍然可用但一旦 Host 开了 autoDeploy容器会重新扫描并覆盖你手工写的 Context表现为「配置明明改了访问路径还是旧的」。更稳的做法是把 context 配置放到应用自己的META-INF/context.xml里跟着应用走升级容器不用改 server.xml。4.3 编码链路GET、POST、JSP 三处各有各的开关中文乱码在 J2EE 项目里几乎是必现问题原因是编码设置分散在三个层面缺一处就断链。第一处是 URL 和查询串的解码由连接器的 URIEncoding 决定8.x 默认 UTF-8一般不用管。第二处是请求体的解码POST 表单提交的内容由request.setCharacterEncoding()决定而它必须在读取任何参数之前调用写在业务代码里很容易漏。!-- WEB-INF/web.xml用容器自带的过滤器统一设置请求体编码 -- filter filter-nameencodingFilter/filter-name filter-classorg.apache.catalina.filters.SetCharacterEncodingFilter/filter-class init-param param-nameencoding/param-name param-valueUTF-8/param-value /init-param init-param param-nameignore/param-name param-valuefalse/param-value /init-param /filter filter-mapping filter-nameencodingFilter/filter-name filter-mapping/*/filter-mapping /filter-mappingignorefalse表示强制覆盖即使业务代码里已经设过别的编码也会被改成 UTF-8这一点在接手老项目时很有用能把散落在各处的重复设置统一掉。需要注意这个过滤器来自容器的 lib 目录跟着容器走如果应用未来要迁到别的容器自己写一个等价的 Filter 更稳妥逻辑就是先 setCharacterEncoding 再放行。第三处是 JSP 页面的输出编码由页面指令控制% page contentTypetext/html;charsetUTF-8 pageEncodingUTF-8 %两个属性都要写pageEncoding 决定源文件按什么编码读取contentType 里的 charset 决定响应头里的字符集。只写一个在中英文混排的页面上就可能出现一半正常一半乱码。响应侧的字符集如果被过滤器或业务代码提前设置过JSP 里的设置不生效排查时先看响应头里的Content-Type实际值。5. 排查清单Windows 下 Tomcat 起不来的五类典型故障这几条基本覆盖了我在 Windows 环境里遇到的绝大多数「起不来」和「起来了但不对」。顺序按发生阶段排从启动到运行每条都写清楚现象、原因和处置动作。5.1 启动阶段闪退、端口占用、类版本不匹配现象一双击 startup.bat黑窗口一闪就消失什么提示都没有。原因是启动脚本在真正拉起 JVM 之前就失败了常见于 JAVA_HOME 未设置、路径里带空格或中文、或者 JRE_HOME 指向了一个不存在的目录。解决方式是不要再双击打开 cmd 执行cd /d ...\bin再catalina.bat run如果第一行就提示The JAVA_HOME environment variable is not defined correctly说明变量问题如果执行到一半才报错报错信息会完整留在控制台。顺带检查 setenv.bat 里有没有写错的路径这个文件是自己加的最容易带着笔误。现象二控制台报java.net.BindException: Address already in use: bind启动中断。原因是 8080 或 8005 被占用可能是上一个 Tomcat 进程没退干净也可能是别的服务占了。解决分两步netstat -ano | findstr :8080找到 PIDtasklist | findstr PID确认是什么进程确认可以结束后taskkill /PID PID /F如果占用者是必须保留的服务就改 server.xml 里的端口。注意 8005 冲突时表现为启动成功但shutdown.bat无效很多人会误以为关闭脚本坏了。现象三部署后访问报UnsupportedClassVersionError。原因是编译用的 JDK 版本高于运行容器的 JDKclass 文件版本号对不上。解决方式是统一版本要么把运行时换成更高版本注意 8.5 这一代对高版本 JDK 的兼容性有限不要盲目上要么在构建配置里把编译目标降到 8。改完记得清掉 work 目录和应用的 classes否则可能还是加载旧产物。5.2 运行阶段404、乱码、会话丢失现象四容器启动正常浏览器访问返回 404日志里没有任何异常。这是最容易被误判成「容器坏了」的一类。原因通常有三个应用名大小写不对Windows 文件系统不敏感但 URL 路径敏感/Demo和/demo是两回事war 包没被解压而应用依赖解压目录里的资源或者 server.xml 里 Host 的 appBase 被改过应用实际没在那个目录下。排查顺序是先看启动日志里有没有Deploying web application archive对应的那条记录没有就是没被扫到有记录但访问 404就去 webapps 下确认目录结构和WEB-INF/web.xml是否存在。现象五页面中文乱码或者登录后下一次请求就掉线。这两个现象常被当成两个问题其实同源。乱码按第 4.3 节的三层链路依次查响应头字符集、请求体编码、JSP 页面指令。会话丢失则先看应用有没有配置sessionCookiePath或者自定义了 cookie 名多应用同域部署时 cookie 互相覆盖是典型原因再检查web.xml里的session-configsession-timeout是不是被设成了很小的值。还有一个隐蔽原因war 包热部署时容器会重建应用默认不持久化会话调试期间频繁改文件就会出现「刚登录就掉线」把autoDeploy关掉或者接受这个代价即可。补充一条经常被忽略的日志文件不生成。现象是 logs 目录空的控制台却有输出。原因是 logging.properties 里配置的 handler 指向了别的目录或者目录权限不足写不进去。Windows 上从别处拷过来的 Tomcat 目录经常带着只读属性整个目录右键属性里去掉只读或者用管理员权限跑一次启动脚本生成文件后再恢复正常权限问题基本能解。6. 把 8.5.38 固化成可复现基线三个自检动作和一个改不掉的习惯同一个包在不同机器上表现不一致八成不是包的问题而是基线没有固化。我的做法是把它当成一个固定环境来维护配置只通过 setenv.bat 和 conf 下的三个文件改其余目录保持原样换机器时把整个目录打包带走而不是在新机器上重新解压再凭记忆配一遍。启动之后先做三个自检确认这套环境是「可复现」的而不是「这次碰巧能跑」rem 自检一确认容器用的 JDK 与预期一致 catalina.bat version rem 自检二确认部署路径与 context path curl -I http://localhost:8080/demo/ rem 自检三确认 access log 已经在写且带耗时字段 type D:\server\apache-tomcat-8.5.38\logs\localhost_access_log.*.txt第一条输出里会打印 JVM 版本和 CATALINA_BASE多实例环境下这是唯一可信的来源第二条只看状态码和响应头不做业务校验能区分「容器没起来」和「应用没部署好」第三条确认日志真的落盘并且格式里有%D没有耗时数据的日志在排性能问题时价值有限。几个值得固化的具体项列在下面都是踩过之后加进去的项目取值理由部署路径D:\server\apache-tomcat-8.5.38无空格、无中文、非系统目录端口HTTP 8080 / 关闭 8005与默认一致减少交接时的沟通成本JVM-Xms512m -Xmx1024m -Dfile.encodingUTF-8调试够用编码不乱部署方式目录部署 autoDeploy 开改 JSP 即时生效排查快日志保留默认按天滚动保留一周手工清理排错要历史但磁盘会满最后说一个改不掉的习惯每次在别人的机器上接手环境我都不先问「你装了什么」而是先跑一遍catalina.bat version和curl -I把输出截图存进排查记录。有太多次是因为对方环境里 JAVA_HOME 指向了另一个 JDK、或者 webapps 里躺着一个同名旧目录导致排查方向整整偏了两小时。从那以后我每次起环境都强制走一遍这三个自检宁可多花三分钟也不要在错误的前提上做推理。希望帮到你。本文还有配套的精品资源点击获取