上周一个朋友找我帮忙,说在Windows服务器上部署JavaWeb项目,跟着网上的教程一步步操作,Tomcat起来了、war包也解压了,可外网就是访问不了。我远程连上去看了五分钟就定位了问题:8080端口在Windows防火墙里放行了,但云控制台的安全组漏掉了入方向的规则。这种错位在Linux服务器上很少出现,因为大家天然会去检查iptables和firewalld,而Windows服务器的部署教程大多停在“装个JDK、扔个war包”这个层面,后面的网络配置、服务自启、日志管理这些真正决定项目能否长期稳定运行的部分,几乎没人完整写过。
这篇文章把我这些年往Windows服务器上部署JavaWeb项目的流程完整梳理一遍,从部署形态选型、基础环境搭建、打包部署,到服务注册、防火墙配置、Docker编排,再到常见问题的排查链路,每一步都尽量写清楚“为什么这么做”和“哪里容易踩坑”。适合三类人看:第一次把项目往Windows服务器放的开发者、帮公司维护内部系统的运维,以及期末或毕设需要部署JavaWeb项目的学生。如果你手里的服务器是Windows Server 2016、2019、2022,或者Windows 10/11长期开机当服务器用,这篇都能直接照着走。
1. 先别急着动手:war包、jar包和服务器现状要搞清楚
1.1 你的项目到底是war包还是jar包
部署方式完全取决于项目形态,这一步搞错,后面全白搭。传统JavaWeb项目,也就是基于JSP和Servlet的SSM框架项目,通常打包成war包,丢到外置Tomcat的webapps目录下,由Tomcat负责解压和运行。而Spring Boot项目默认打包成jar包,内置了Tomcat,直接java -jar就能启动,不需要额外安装Tomcat。
我见过很多人在这一步就绕了远路。明明是个Spring Boot项目,非要把war包扔进Tomcat,结果启动报错或者类冲突;反过来,传统SSM项目非要让Spring Boot内嵌容器去跑,折腾半天不如直接用Tomcat。判断方法很简单:打开项目的pom.xml,看<packaging>标签,war就是war,jar就是jar。再看有没有spring-boot-maven-plugin,有就是Spring Boot项目。
这里还有一个特殊情况:Spring Boot项目也可以打成war包部署到外置Tomcat,需要修改打包方式、继承SpringBootServletInitializer、排除内嵌Tomcat依赖。但这种做法意义不大,除非是公司现有运维体系强依赖外置Tomcat管理。个人项目或者新项目,Spring Boot老老实实打jar包就好。
| 部署形态 | 适用项目 | 运行时依赖 | 启动方式 | 维护难度 |
|---|---|---|---|---|
| war包 + 外置Tomcat | 传统SSM、Spring MVC、JSP项目 | JDK + Tomcat | 启动Tomcat自动部署 | 中等,要关心Tomcat本身 |
| jar包 + 内嵌Tomcat | Spring Boot项目 | 只需要JDK | java -jar | 较低,进程交给服务工具管 |
1.2 服务器配置摸底和路径规划
别一上来就装环境,先花十分钟搞清楚服务器现状。远程桌面登录后,按Win + R运行winver查看系统版本,Windows Server 2016、2019、2022的常用命令基本一致,不用太担心版本差异。然后在命令行里执行systeminfo,看内存和磁盘剩余空间,尤其要注意C盘空间,很多服务器默认系统盘只有40G,装完系统和补丁就剩不了多少,Java应用再往C盘塞,迟早把磁盘撑爆。
接着检查已经安装的软件和服务。在服务器上打开任务管理器,切到“服务”标签页,或者命令行执行services.msc,看看有没有装过MySQL、Redis、Nginx、IIS、Docker这些。尤其是端口占用情况,先执行netstat -ano | findstr 8080看看8080端口现在有没有被占用,如果已经有一个程序在监听,你的Tomcat启动时会直接报端口冲突。
路径规划是我一直在强调但很多人忽略的点。项目文件、JDK、Tomcat、数据目录,全部放D盘或者其他数据盘,不要放C盘Program Files。原因有两点:第一,Windows系统更新、权限控制都可能影响C盘下文件的读写,Tomcat解压war包、程序写日志经常报权限不足;第二,Program Files中间有空格,某些脚本和批处理处理起来容易出问题。我习惯的目录结构是这样的:
D:\app\jdk D:\app\tomcat D:\app\projects\app.jar D:\app\logs D:\data\mysql D:\data\redis所有环境集中在一个根目录下,备份、迁移、清理都方便。
1.3 远程操作和文件传输方案
部署过程中需要频繁在服务器和本地之间传文件,war包或者jar包动辄几十上百兆,用远程桌面自带的剪贴板复制粘贴不靠谱,经常传到一半断掉,而且某些格式的文件还会被安全策略阻断。我的建议是安装OpenSSH Server,在“设置”->“应用”->“可选功能”里添加,或者用PowerShell执行Add-WindowsCapability -Online -Name OpenSSH.Server~~~~0.0.1.0。装好后用本地的WinSCP、xftp或者直接用scp命令传文件,目录拖拽、断点续传都支持,比远程桌面复制稳太多。
日常操作我推荐优先用OpenSSH加命令行,而不是远程桌面。原因很简单:远程桌面断开重连不影响后台进程,但容易让人养成“看到图形界面就能解决问题”的错觉,而命令行操作可以沉淀成脚本,下次部署直接复用。当然,装系统级软件、改防火墙规则这类操作,远程桌面还是更直观,两个结合着用。
2. 搭好运行环境:JDK、Tomcat和数据库的安装细节
2.1 JDK版本选择和安装时的三个坑
JDK版本怎么选?打开项目pom.xml,看<java.version>标签或者<maven.compiler.source>标签,项目写的是Java 8就用JDK 8,写的17就用JDK 17。Spring Boot 2.x系列一般用JDK 8,Spring Boot 3.x必须用JDK 17以上。这里千万别自作聪明装个最新版JDK 21,很可能项目编译版本太低直接跑不起来。
JDK安装包可以从Oracle官网下载,也可以从Adoptium下载Temurin OpenJDK。个人项目我推荐Temurin,开源协议友好,不需要注册Oracle账号,版本管理也清晰。安装路径设置到D:\app\jdk\jdk-17这种无空格路径,然后配置环境变量。
环境变量配置是这个环节最大的坑集中地。右键“此电脑”->“属性”->“高级系统设置”->“环境变量”,在“系统变量”里新建JAVA_HOME,变量值填JDK的安装根路径,注意不要带bin目录,也不要结尾加反斜杠。然后在Path变量里新建一行%JAVA_HOME%\bin。配置完成后,关掉所有已经打开的cmd窗口,重新开一个,执行java -version验证。
这里有个细节很多人不知道:配置环境变量时,系统变量和用户变量是叠加的,如果之前在用户变量里配过一个旧JDK路径,系统变量里又配了新路径,命令行里java -version显示的可能是旧的。排查时执行where java,会列出所有匹配的可执行文件路径,按顺序从上到下就是实际调用顺序。我遇到过一台服务器上残留了三个JDK,环境变量指向的和实际运行的根本不是同一个,项目启动报各种莫名其妙的版本错误,最后就是靠where java定位清理干净的。
2.2 Tomcat安装和启动:解压比安装更靠谱
Tomcat在Windows上不需要安装程序,直接下载zip压缩包解压就行。到Apache Tomcat官网下载对应版本,注意选择64位Windows zip包。解压到D:\app\tomcat,然后设置一个CATALINA_HOME环境变量指向这个目录,Path里添加%CATALINA_HOME%\bin。
进入D:\app\tomcat\bin目录,双击startup.bat启动Tomcat。这里会弹出一个黑色命令行窗口,千万别关,窗口关闭等于Tomcat退出。启动成功后浏览器访问http://localhost:8080,能出现Tomcat默认页面就算成了。
如果启动失败,去D:\app\tomcat\logs目录看catalina.日期.log文件,端口占用、JDK版本不对、环境变量没配置,日志里都会明确写出来。Tomcat的server.xml在D:\app\tomcat\conf目录下,里面可以配置三个端口:8005是关闭端口、8080是HTTP访问端口、8009是AJP端口。AJP如果不用,建议直接注释掉,历史上出现过多个AJP漏洞,没必要暴露不用的服务。
内存参数在catalina.bat里配置,在文件的set "JAVA_OPTS=%JAVA_OPTS%..."这一行前后追加参数。简单一点,直接在文件开头加一行固定配置:
set JAVA_OPTS=-Xms512m -Xmx1024m -XX:MaxMetaspaceSize=256m-Xms是初始堆大小,-Xmx是最大堆大小。设置前先看服务器物理内存,比如一台4G内存的服务器,Tomcat分配1024M,MySQL分配大概500M,还剩下足够系统运行。不要贪心,堆内存不是越大越好,设置过大会导致系统内存耗尽。
2.3 MySQL和Redis在Windows上的安装配置
MySQL建议用MSI安装包,安装过程会自动注册成Windows服务,省去手动初始化的麻烦。安装时选择“Server only”模式,字符集选utf8mb4,密码设置一个强密码,不要用root加个简单密码直接部署到公网,迟早被扫库。
如果要用zip解压方式,步骤也不复杂:解压到D:\mysql,在目录下新建my.ini配置文件:
[mysqld] basedir=D:/mysql datadir=D:/mysql/data port=3306 character-set-server=utf8mb4 collation-server=utf8mb4_unicode_ci bind-address=0.0.0.0 [client] default-character-set=utf8mb4然后用管理员权限打开cmd,进入D:\mysql\bin,先执行mysqld --initialize-insecure生成初始化数据目录,这时候root用户是空密码,再执行mysqld --install MySQL --defaults-file="D:\mysql\my.ini"把MySQL注册成服务,最后net start MySQL启动。
启动后立即处理两件事:第一,给root设置密码;第二,创建应用专用账号,不要用root连接JavaWeb项目。SQL是这样:
CREATE USER 'javaw'@'%' IDENTIFIED BY 'Javaw@2024'; GRANT ALL PRIVILEGES ON *.* TO 'javaw'@'%'; FLUSH PRIVILEGES;如果项目和应用在同一台服务器,host用localhost更安全,但很多场景要支持运维从外部连数据库,所以这里用%。安全考量上,这个账号只给app库的全部权限,不给所有库权限,更规范。
Redis在Windows上没有官方支持版本,我用的比较多的是tporadowski在GitHub上维护的Redis for Windows发行版。下载zip解压后,进入目录执行安装服务命令:
redis-server --service-install redis.windows.conf --service-name Redis修改redis.windows.conf文件,重点是两个参数:requirepass设置访问密码;appendonly yes开启持久化,防止重启丢数据。如果只有本机的Java应用需要连Redis,bind保持127.0.0.1不要改,减少暴露面。
3. 打包和部署:war包与jar包方式的全流程操作
3.1 打包前必须核对的配置清单
部署最怕的不是不会打包,而是打包前没确认配置,部署完启动失败,又要回去改配置重新打包。我每次打包前都会花五分钟过一遍这几项。
第一,数据库连接配置。找到配置文件里的spring.datasource或jdbc相关配置,确认数据库地址、端口、库名、账号、密码是生产环境的。很多人本地连的是localhost,上传到服务器后没改,启动时数据库连接报错。
第二,文件存储路径。JavaWeb项目通常涉及文件上传下载,如果配置文件里写死了绝对路径,比如D:\workspace\uploads,而服务器上没有这个目录,程序运行时会报FileNotFoundException。Windows路径建议用D:/uploads这种正斜杠写法,兼容性更好。
第三,激活的配置文件profile。Spring Boot项目常用application-dev.yml和application-prod.yml区分环境,部署时要在启动命令里显式指定--spring.profiles.active=prod,否则默认加载的是dev配置。
确认无误后执行打包命令:
mvn clean package -DskipTests加-DskipTests的意思是跳过单元测试,因为测试环境很可能连接不上数据库或者依赖其他外部服务,强行跑测试只会拖慢打包速度。打包完成后,target目录下会生成war或jar文件。
3.2 war包部署到Tomcat:最容易被忽略的旧包残留问题
把生成的war包复制到D:\app\tomcat\webapps目录下。如果Tomcat正在运行,它会自动检测到新文件并解压。如果没运行,启动Tomcat时会自动处理。访问路径是http://服务器IP:8080/war包名/,比如war包叫myweb.war,访问路径就是http://IP:8080/myweb/。
有个点必须提醒:更新部署时,一定先停Tomcat,然后删除webapps下旧的war包和解压出来的同名目录,再放新的war包进去。如果不删旧目录,Tomcat解压新war包时会覆盖部分文件,但残留的旧配置文件、编译后的class文件可能还在,造成“改了配置但没生效”“代码好像还是老的”这种诡异问题。我踩过不止一次,现在养成习惯:更新版本就干净删除,不留旧文件。
如果想让项目直接通过http://IP:8080/访问,不带项目名路径,把war包改名为ROOT.war即可,这是Tomcat约定的根应用名称。
3.3 jar包方式部署Spring Boot项目
Spring Boot的jar包部署更简单,但需要注意的事情一点也不少。进入jar包所在目录,执行:
java -jar app.jar --spring.profiles.active=prod --server.port=8080如果jar包的配置文件需要外置,Spring Boot会自动读取jar包同级config目录下的application.yml。这个特性非常实用,意味着部署时不用重新打jar包,直接改服务器上的配置文件就能调整环境。
启动之后,开一个cmd窗口,执行jps -l看Java进程是否存活;再执行netstat -ano | findstr 8080看端口是否监听;最后浏览器访问http://localhost:8080验证。
这里有一个Spring Boot部署的经典问题:项目启动成功了,localhost访问正常,但用局域网IP访问不了。原因是内嵌Tomcat默认绑定的是0.0.0.0,理论上没问题,但如果你在application.yml里显式配置了server.address=127.0.0.1,那就只有本机访问得了。排查时先确认这个配置项,再检查防火墙和安全组,别一上来就怀疑防火墙。
4. Windows服务器独有的内容:服务注册、开机自启、日志管理
4.1 为什么你的Tomcat窗口一关项目就没了
在Windows上直接运行startup.bat或者java -jar,进程是附着在cmd窗口上的,这个窗口一关,进程就被系统终止。Windows Server重启后,这些进程更不可能自己回来。这就是Windows和Linux最大的差别:Linux有systemd,可以定义服务的开机自启和异常重启,Windows默认没有这套机制,需要额外借助工具。
解决思路是给Java进程套一层系统服务的外壳。常用的两个工具是WinSW和NSSM,二选一就够,我个人用得比较多的是WinSW,配置全部集中在一个XML文件里,清晰好维护。
先去GitHub下载WinSW-x64.exe,放到D:\app\services目录,重命名为myapp-service.exe。同一目录下新建一个myapp-service.xml:
<service> <id>myapp-service</id> <name>JavaWebApp Service</name> <description>JavaWeb Application Service</description> <executable>java</executable> <arguments>-Xms512m -Xmx1024m -jar D:\app\projects\app.jar --spring.profiles.active=prod</arguments> <logpath>D:\app\services\logs</logpath> <logmode>rotate</logmode> </service>用管理员权限打开cmd,进入D:\app\services目录,执行:
myapp-service.exe install net start myapp-service到这一步,JavaWeb项目就变成Windows服务了。系统服务管理器里可以看到它,属性里把启动类型设为“自动”,以后开机自动运行。卸载服务时先net stop myapp-service,再执行myapp-service.exe uninstall。
需要注意,executable写的是java,依赖PATH环境变量能找到java命令。如果服务器上装了多个JDK,建议把executable改成JDK实际路径,比如D:\app\jdk\jdk-17\bin\java.exe,避免指向错误版本的JDK。
4.2 Tomcat作为war包容器的服务化
如果走war包部署方式,也可以把Tomcat注册成服务。Tomcat的bin目录下自带service.bat,提前设置好CATALINA_HOME环境变量,管理员cmd运行D:\app\tomcat\bin\service.bat install,Tomcat就会注册为一个名为Tomcat的服务。如果这个方式有问题,用NSSM包装一下也简单,运行nssm install Tomcat,在界面里把Application指向D:\app\tomcat\bin\startup.bat,启动目录指向D:\app\tomcat\bin。
4.3 日志管理和磁盘空间防护
服务化之后,日志管理成为长期稳定运行的关键点。WinSW自带日志轮转能力,<logmode>rotate</logmode>表示按大小分割,不会让单个日志文件无限膨胀。应用程序自己打的日志,比如logback、log4j2,在配置文件里配好滚动策略,日志文件按天切分,保留最近30天就够。
Tomcat的logs目录需要特别注意。Tomcat运行时间长了,catalina.out、localhost_access_log这些文件会越来越大。我习惯写一个批处理脚本,通过Windows计划任务每天执行一次,清理7天前的日志:
forfiles /p D:\app\tomcat\logs /s /m *.log /d -7 /c "cmd /c del @file" forfiles /p D:\app\projects\logs /s /m *.log /d -7 /c "cmd /c del @file"计划任务添加方式:控制面板->管理工具->任务计划程序->创建基本任务,触发器选“每天”,操作选“启动程序”,把脚本路径填进去。这一步做了,服务器长期运行基本不会因为日志把磁盘塞满。
5. 从本机能开提到外网能开:防火墙、安全组、端口和域名
5.1 Windows防火墙放行端口的正确姿势
很多人的部署流程走到“浏览器访问localhost通了”就停了,但实际用户访问的是外网IP,这时候就会卡住。第一步检查Windows防火墙。打开“控制面板”->“Windows Defender防火墙”->“高级设置”->“入站规则”->“新建规则”,选择“端口”->“TCP”->“特定本地端口”填8080,选“允许连接”,配置文件全勾选,规则名称随便填。
用命令行更快,管理员cmd执行:
netsh advfirewall firewall add rule name="Open 8080" dir=in action=allow protocol=TCP localport=80805.2 云服务器安全组是最容易被漏掉的一环
Windows防火墙放行之后,如果外网还是不通,接着查云控制台的安全组。阿里云、腾讯云、华为云的ECS实例,都有安全组规则,相当于在虚拟机外面又套了一层防火墙,Windows防火墙放行只是第一层,安全组不放行,流量根本到不了服务器网卡。
在云控制台找到对应的实例,进入安全组配置,添加入方向规则:协议端口填TCP:8080,授权对象填0.0.0.0/0(或者你公司出口IP,更安全)。这一步操作完,再测外网基本就通了。
一个通用的排查链路是这样:telnet 公网IP 8080,通,说明网络链路没问题,问题在应用层;不通,用排除法,先关Windows防火墙测试(注意:生产环境别随便关,测完要开回去),不行再查安全组;安全组没问题就查程序是不是只监听了127.0.0.1。
我在开头提到的那个朋友,他的问题就是这一步:Windows防火墙放行了,安全组漏掉了。前后排查不到十分钟,但他自己折腾了一个下午。
5.3 80端口冲突和IIS的宿怨
很多人希望直接用80端口访问项目,不用带端口号。但Windows服务器上最常见的80端口占用来自IIS。如果服务器的“World Wide Web服务”处于运行状态,80端口就是IIS的,Tomcat启动时会报Address already in use。
解决方案有两种:第一,停用IIS,控制面板->“启用或关闭Windows功能”->取消勾选“Internet Information Services”,重启生效;第二,给Tomcat换个端口,比如8080,然后用Nginx做反向代理监听80,把请求转发到8080。第二种方案更灵活,也是我在生产环境推荐的做法,因为Nginx还能顺便处理静态资源、HTTPS证书、多个应用的路由分发。
排查端口占用用这三条命令:
netstat -ano | findstr :80 tasklist | findstr PID taskkill /F /PID 进程ID先看到底是哪个进程占了80端口,确认能杀再杀。
5.4 域名、HTTPS与Nginx反向代理
项目部署到公网后,最好用域名访问而不是一长串IP加端口。域名从云厂商购买后,解析到服务器公网IP就完成绑定。如果是国内服务器,域名解析后还要按云厂商指引完成相应的合规流程,这个在云控制台会有明确提醒,按流程走就行。
HTTPS证书现在基本都是免费的了,云厂商提供一年期的免费SSL证书。部署到Tomcat的方式有两种:一种是在server.xml里配置证书文件,然后用Tomcat直接监听443端口;另一种是Nginx监听443并配置证书,把请求反向代理到后端8080。我更推荐后者,因为证书到期替换、多个应用共用一个证书、HTTP自动跳转HTTPS这些操作在Nginx里都更简单。
Windows上安装Nginx非常省事,下载Windows版zip解压到D:\app\nginx,修改conf\nginx.conf,一个最基础的反向代理配置:
server { listen 80; server_name yourdomain.com; location / { proxy_pass http://127.0.0.1:8080; proxy_set_header Host $host; proxy_set_header X-Real-IP $remote_addr; proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; } }然后执行start nginx启动,浏览器访问域名就直接转到JavaWeb项目了。Nginx在Windows上以非服务方式运行,建议配合WinSW把nginx.exe也注册成服务,方法和Java应用一样。
6. 环境混乱时的另一种解法:用Docker Compose编排整个JavaWeb栈
6.1 Windows上Docker的安装前提
如果你的Windows服务器上已经装了乱七八糟一堆软件,MySQL一个版本、Redis一个版本,互相掺杂很难管;或者你需要在多台Windows服务器上复现同一套环境,这时候用Docker是更好的选择。
Windows上装Docker Desktop,底层依赖WSL2。安装前在PowerShell(管理员)里执行:
wsl --install装完WSL2并设置默认版本后,去Docker官网下载Docker Desktop安装包。安装过程中选择使用Linux容器。装好后,后续的docker、docker-compose命令用法和Linux上基本一致。
需要说明的是,Docker Desktop对大型企业有商业授权要求,如果是在公司生产环境用,使用前确认一下许可情况。个人开发和中小团队测试环境,免费版本完全够用。Windows Server老版本如果装不了Docker Desktop,要用Docker Enterprise或者干脆上一台Linux虚拟机,但这些都不是纯Windows部署的范畴了。
6.2 用docker-compose.yml一锅端编排MySQL、Redis和应用
我这里给一个可以直接套用的docker-compose.yml模板,包含MySQL、Redis和Java应用三个容器:
version: '3.8' services: mysql: image: mysql:8.0 container_name: javaw-mysql restart: always environment: TZ: Asia/Shanghai MYSQL_ROOT_PASSWORD: Root@2024 MYSQL_DATABASE: javawdb MYSQL_USER: javaw MYSQL_PASSWORD: Javaw@2024 command: - --character-set-server=utf8mb4 - --collation-server=utf8mb4_unicode_ci ports: - "3306:3306" volumes: - D:/docker-data/mysql:/var/lib/mysql redis: image: redis:7 container_name: javaw-redis restart: always command: redis-server --requirepass Redis@2024 --appendonly yes ports: - "6379:6379" volumes: - D:/docker-data/redis:/data app: image: openjdk:8-jre container_name: javaw-app restart: always depends_on: - mysql - redis ports: - "8080:8080" volumes: - D:/app/projects/app.jar:/app/app.jar - D:/app/projects/config:/app/config - D:/app/projects/logs:/logs command: java -Xms256m -Xmx512m -jar /app/app.jar --spring.profiles.active=prod为什么需要把数据目录挂载到Windows的D盘?因为容器重建或者升级镜像后,容器内部的数据会全部丢失。把MySQL数据、Redis持久化文件映射到宿主机目录,才能保证数据安全。这个细节在生产环境尤其重要,我见过不下三次因为容器删除导致数据库数据全没了的案例,都是没挂载数据卷。
在这个编排里,应用镜像用的是通用openjdk镜像,jar包通过挂载方式放入容器,好处是更新代码时只需要替换jar包,不用重新构建镜像。如果项目依赖特殊环境,也可以写一个Dockerfile把jar包打进去:
FROM openjdk:8-jre MAINTAINER yourname COPY app.jar /app/app.jar WORKDIR /app EXPOSE 8080 ENTRYPOINT ["java", "-jar", "app.jar"]然后docker-compose.yml里的app服务改成build: .,用docker-compose up -d一条命令拉起整个环境。
6.3 Docker方案和传统方案的取舍
用Docker部署不是银弹,它解决的是“环境一致性”和“依赖管理”两个问题,但在Windows上也有代价。
| 对比维度 | 传统方式(JDK+Tomcat+MySQL) | Docker方式 |
|---|---|---|
| 环境隔离 | 弱,多个服务共享系统环境 | 强,每个容器独立 |
| 版本管理 | 手动安装卸载,容易残留 | 镜像版本清晰,切换方便 |
| 迁移复现 | 需要重新配环境 | 拷贝docker-compose.yml即可 |
| 性能开销 | 较低,进程直接跑在系统上 | 有一定开销,尤其磁盘读写 |
| 资源占用 | 按需分配 | Docker Desktop本身占用较高 |
| 运维复杂度 | 要管理多个服务 | docker-compose统一管理 |
如果你是个人的小型项目,或者服务器配置一般,传统方式足够;如果项目依赖很多,或者需要在多台机器上快速部署,用Docker。这两种方案不是互斥的,很多人先传统方式跑通了,后来嫌环境复杂才切Docker,切换成本也不高。
7. 部署后的故障排查链路与几条压箱底的经验
7.1 最常见的五个部署失败场景
Windows服务器上部署JavaWeb项目,失败场景翻来覆去就那么几个,我列个对照表,遇到问题直接按表查。
| 现象 | 可能原因 | 排查方式 |
|---|---|---|
| 服务已启动但外网访问超时 | 云安全组未放行,或Windows防火墙未放行 | 先telnet公网IP 8080,再查安全组和防火墙 |
| localhost能访问但局域网IP不行 | 程序绑定了127.0.0.1 | 检查server.address配置,改为0.0.0.0 |
| 启动报数据库连接失败 | 数据库未启动、账号不允许远程、密码错、驱动版本不匹配 | 连接串在本地用数据库客户端测试 |
| 页面中文乱码 | TomcatURIEncoding未设置、连接串缺characterEncoding、页面meta缺失 | 统一使用UTF-8,Tomcat URIEncoding改为UTF-8 |
| 重启后项目全丢了 | 没注册Windows服务,cmd窗口关闭进程被终止 | 用WinSW注册服务,启动类型设为自动 |
7.2 一套标准的排查链路
部署失败后别乱试,按顺序执行下面的排查,效率最高。
第一步,确认进程在不在。命令行执行tasklist | findstr java,如果输出为空,说明Java进程没起来,去看程序日志。
第二步,确认进程变绿了但端口没监听。执行netstat -ano | findstr 8080,没有任何输出说明程序没绑定这个端口,要么配置文件里server.port不是8080,要么启动报错之前就退出了。
第三步,确认端口监听没问题但本机访问不了。在服务器上用浏览器打开http://localhost:8080,能打开说明应用正常,问题在网络层;打不开说明应用本身有问题,去看Tomcat的catalina日志或Spring Boot的启动日志。
第四步,本机能打开但外网不行。先telnet 公网IP 8080,不通就挨个检查Windows防火墙、云安全组;通了再看是不是域名解析或HTTPS证书配置问题。
这四步走完,90%的问题都能定位。关键是每一步要记录结果,不要靠感觉猜测,否则很可能绕着圈子浪费时间。
7.3 几条压箱底的经验,都是实操换来的教训
Windows服务器不比Linux,系统更新、补丁安装频率高,每次更新完都可能重启服务器。如果Java进程没有做成服务,重启后不会自动拉起,这就是很多项目跑着跑着突然数据收集日志断了、网页打不开的原因。我现在的习惯是:不管jar包项目还是Tomcat,全部注册成Windows服务,并且把服务的恢复策略改成“失败后自动重启”。在服务属性里能找到,重启服务、延迟重启时间都设好,Java进程崩溃了系统会自动拉起来。
关于编码,Windows默认使用GBK,和Linux的UTF-8不一致。部署JavaWeb项目时,如果发现中文乱码,先检查三处:数据库连接串有没有加characterEncoding=utf8,Tomcat的server.xml里Connector有没有配URIEncoding="UTF-8",项目本身的代码和页面文件是不是UTF-8编码。这三处一致了,乱码问题基本绝迹。
关于数据库密码,不要写死在配置文件里然后随手传到服务器。至少把密码放到Spring Boot的application-prod.yml外置配置里,文件权限设置成只有管理员可读写。更稳妥的方式是使用环境变量,配置项里写${DB_PASSWORD},启动服务前在系统环境变量里设置好。这样即使配置文件泄露,密码也不会跟着暴露。
最后,每次部署前备份现有版本。我会在D盘建一个backup目录,把上一个能正常运行的war包或jar包、数据库导出文件放到里面,用日期命名。一旦新版本出问题,十分钟内就能回滚到旧版。这套习惯救过我太多次。
最后再分享一个小技巧
我在Windows服务器上部署JavaWeb项目积累的一个小习惯是:把所有部署相关的脚本和配置文件统一放在D:\app\scripts目录,包括Windows服务的XML配置、Nginx的conf副本、计划任务的bat脚本。这样服务器哪怕完全重装,我按照scripts目录里的文件一步步操作,半小时内就能复现整套环境。这个目录本身,就是这台服务器最完整的“部署文档”。