做Java后端的,基本上都会撞上这么一天:本地用SSM框架写得好好的项目,一放到Linux云服务器上就各种不适应,要么数据库连不上,要么端口访问不了,要么干脆启动就报错。这篇文章就把SSM项目通过Tomcat部署到Linux云服务器的完整链路拆开讲透,从本地改配置、Maven打包,到服务器装JDK、Tomcat、MySQL,再到放行端口、上线验证,每一步都给你可以直接照着操作的命令和不踩坑的理由。
不管你是学生交课程设计,还是刚工作的开发第一次独立上线项目,又或者是想搞明白部署原理的运维新人,这套流程都适用。我不会讲太虚的东西,所有内容都是我从实际部署当中反复试出来的,版本坑、防火墙坑、日志坑都会提到,你照着走,能少走很多弯路。
1. 部署前的整体规划:先想清楚再动手,别急着敲命令
很多人一拿到服务器就开始下载JDK、装Tomcat,结果折腾一晚上发现项目还是跑不起来。其实部署SSM项目这件事,功夫在“动手之前”。先把下面这几件事想清楚,后面每一步都是在填你已经规划好的坑位。
部署的本质,就是把你在本地开发环境已经验证过的代码,搬运到一个干净、独立的生产环境里重新组装。本地环境有IDE、有本机数据库、有一堆开发辅助工具,服务器上什么都没有,所有依赖都要从零铺开。所以第一步不是问“怎么装Tomcat”,而是问“我的项目到底需要什么”。
1.1 先搞清楚SSM项目跑起来需要哪三样东西
SSM是Spring、SpringMVC、MyBatis三者的组合。它的运行模式是:客户端发HTTP请求,Tomcat接收后交给SpringMVC做路由分发,Service层处理业务逻辑,MyBatis负责操作数据库完成持久化。从这个流程可以推导出,它最少依赖三样东西:Java运行环境、Web容器、数据库。
Java运行环境对应JDK,这是所有Java程序的地基,没它连javac、java命令都跑不起来。Web容器对应Tomcat,它负责实现Servlet规范、监听端口、管理请求线程。数据库对应MySQL,SSM项目的数据几乎都存在MySQL里,除非你的项目接的是别的数据库或者内存库。
这三样东西在服务器上的安装顺序一般也是JDK优先、Tomcat其次、MySQL最后,因为Tomcat安装脚本本身就要用到JAVA_HOME环境变量,而MySQL虽然独立,但项目启动时连接数据库的成功与否直接决定服务是否能对外提供完整功能。搞清楚这个依赖关系,你就不至于装完Tomcat才发现JDK没配好,白折腾一趟。
1.2 部署方案选型:为什么坚持手动部署Tomcat
现在部署Java项目的方案非常多,Docker容器化、Kubernetes编排、Jenkins持续集成,还有各种自动化平台。但对一个SSM项目来说,手动部署Tomcat依然有它不可替代的价值,尤其对刚接触部署的人来说,这就是基本功。
手动部署的好处是每一步都透明。JDK装在哪个目录、Tomcat跑在哪个端口、配置文件的哪一行影响什么行为,你全部可控。Docker确实能一键起环境,但如果遇到容器内部日志看不到、时区不对、容器重启丢失配置这类问题,不懂底层的人连排查方向都没有。我见过太多用Docker部署的开发者,出了问题只会删了容器重新run,治标不治本。
单台云服务器、中小型SSM项目、访问量不大,手动部署在资源占用上也是最省的。Tomcat本身就是一个进程,不需要额外跑守护程序,内存占用可控。以后项目复杂了再迁移到容器化也不冲突,因为你已经理解了每个组件干什么的,迁移只是换个方式启动罢了。
1.3 部署前要准备的资源清单
在做任何操作之前,建议你先建一个文本文件,把下面这些东西列清楚。这不是形式主义,是防止你在部署过程中反复犯低级错误。
- 一台Linux云服务器,建议2核4G起步,这是底线,1G内存跑Tomcat加MySQL会非常吃力,动不动就OOM
- 一个SSH终端工具,命令行操作要用
- JDK安装包,建议下载tar.gz版本,不要用系统包管理器自带的旧版本
- Tomcat安装包,tar.gz版本即可
- MySQL数据库,版本和本地开发库保持一致
- Maven构建工具,本地打包要用
- 数据库连接客户端,比如命令行或者图形工具,用来初始化和验证MySQL
- SSM项目的完整源码或者已经打好的War包
清单里每一项缺失都会造成中断。我遇到过好几次的情况是:服务器装到一半发现JDK下错版本,Tomcat启动JVM直接报UnsupportedClassVersionError,又从头来过。提前核对版本,等于把最容易浪费时间的坑提前填了。
1.4 版本兼容性:部署时最容易被忽视的第一道坎
版本兼容性这个问题,我在部署和帮别人排查时遇到太多次。一个典型场景是:本地用的JDK 8,项目里用了高版本的Spring和MyBatis,本地跑得好好的。服务器上yum默认装了一个JDK 17,Tomcat 9 倒是启动正常,但项目一部署就报错,因为字节码版本对不上。
版本搭配有一个基本对照:JDK 8可以用Tomcat 8.5或9,JDK 11以上可以用Tomcat 9或10,Tomcat 10及以上的包名从javax.改成了jakarta.,这会导致老项目里import javax.servlet报错,除非你更新代码。所以稳妥的组合是JDK 8加Tomcat 8.5或9加MySQL 5.7(或MySQL 8,但要配合新版本驱动)。
数据库版本也容易触雷。MySQL 8的默认认证插件是caching_sha2_password,老版本的mysql-connector-java驱动连接时会报Public Key Retrieval is not allowed。解决办法是换8.0+驱动,或者连接URL带上allowPublicKeyRetrieval=true和useSSL=false。我建议部署前先统一版本策略:本地用什么数据库版本,服务器就装什么版本,驱动也统一,能少掉一大半的兼容性错误。
以上这些事想清楚了,你再动手装环境,每一步都有方向感。
2. 本地代码调整与打包:服务器上的一切问题,都是从这里带过去的
服务器装好之前,还有一件特别重要的事要在本地完成:把代码调整成能被“生产环境”使用的状态,并且打成一个标准的War包。这一步被很多人忽略,他们直接从IDE里跑一下项目,觉得能启动就上传,结果到了服务器上各种路径错误、配置错误,排查起来极其痛苦。
2.1 多环境配置:别让本地数据库密码跑到生产环境里
SSM项目里最常见的配置是jdbc.properties或者application.properties,里面写着数据库地址、账号、密码。本地开发时通常连的是127.0.0.1,密码简单,比如123456。如果你直接把这份配置打进War包带到服务器上,项目启动时就会尝试连本机的数据库,而服务器上虽然可能装了MySQL,但用户和密码都对不上,于是报Access denied。
解决办法是做多环境配置,用Spring的Profile机制或者Maven的Profile机制区分开发、测试、生产环境。以Maven为例,在pom.xml里定义多个profile,分别指定资源目录,比如src/main/resources-dev和src/main/resources-prod,每个目录下放各自的配置文件。打包时加上参数-Pprod,就能把生产配置打进去。
生产环境的配置项里面,数据库地址建议填服务器内网IP,因为MySQL部署在同一台机器上时用内网IP比外网IP更快更安全。密码不要沿用本地弱口令,单独设一个强密码。配置文件中如果有文件上传路径、日志输出路径,也要改成Linux路径,比如/opt/app/upload,而不是Windows下的D:/upload。
我之前就是没做多环境隔离,把本地密码带上了生产,结果数据库被扫到弱口令,服务器遭了殃。从那以后,我打包前必做一次配置检查,把本地的绝对路径、测试开关全部扫一遍。
2.2 用Maven打War包:命令和注意事项
SSM项目是用Maven管理的,打包命令看着简单,但用不好也会出问题。推荐的命令是:
mvn clean package -Dmaven.test.skip=trueclean会删除之前的target目录,保证打包结果干净;package会按照pom.xml里的packaging标签执行打包,如果packaging是war,就会生成War包。这里有个细节:很多教程写的是-DskipTests,意思是跳过测试执行但会编译测试类。在有些项目里,测试类会连接本地数据库,编译不报错但打包时测试跑起来会卡住甚至失败。所以我会用-Dmaven.test.skip=true,它连测试代码的编译都跳过,打包更快更稳。
还有一个高频坑:如果你用的是IDE,千万别直接选择IDE自带的Artifacts来打War包,或者从target目录里手工拷文件。这种方式打出来的包可能不包含依赖的jar包,或者包含的是不完全的class文件。必须通过Maven命令来打,这样依赖解析和资源过滤都会按pom.xml执行。
打完包后,target目录下会生成一个xxx.war文件。可以用压缩工具打开看看,内部应该包含WEB-INF/classes目录(这里是编译后的class文件)、WEB-INF/lib目录(这里躺着所有依赖jar包,包括Spring、MyBatis、数据库驱动等)、META-INF目录。看到lib目录里有东西,说明打包正常。
2.3 上传前自检:War包里最容易藏雷的三个地方
打包完成不等于可以上传了。我会在每次部署之前,用压缩工具或者命令行打开War包,做一次“考古式”检查。重点看三个方面。
第一,检查配置文件。解压出WEB-INF/classes下面的jdbc.properties或者application-prod.properties,确认数据库地址是服务器的IP或者域名,用户名密码对得上。这一步能避免九成以上的启动失败。
第二,检查日志路径。项目里如果配置了log4j或者logback,日志输出路径可能是Windows盘符格式,比如D:/logs,部署到Linux以后目录不存在,日志写不进去,表面上看项目启动起来了但没有任何日志输出,排查问题就会变成瞎子。最好改成相对路径比如./logs或者/var/log/项目名,并且在服务器上提前建好目录。
第三,检查静态资源的相对路径。有些项目在HTML或者JS里写死了资源的绝对路径,比如/img/xx.png,部署到服务器后在项目上下文路径下访问不到。这个问题和部署本身关系不大,但上了服务器之后更容易暴露,因为本地IDE帮你做了很多路径适配,而Tomcat不会。
做完这三项检查,War包才算真正具备上传条件。这一步花十分钟,省下来的可能是整个下午的排查时间。
3. 服务器环境搭建:JDK、Tomcat、MySQL,一次装到位
到了服务器端,顺序很重要。我会严格按照JDK、MySQL、Tomcat的顺序来,因为Tomcat启动时需要JDK,项目启动时需要MySQL,先装好底层依赖,后面排错时能一眼看出问题是出在哪个环节。
3.1 安装并配置JDK:版本选型和环境变量
我最推荐的方式是直接把tar.gz包解压到Linux指定的目录,而不是用yum install java-1.8.0-openjdk。原因是系统包管理器默认装的JDK版本和路径不完全可控,有时装完了还没有jps、jstat这些诊断工具,你得额外装openjdk-devel才能用。而手动解压的方式,位置固定,工具齐全,想切换版本时改一下环境变量就行。
具体步骤大致是这样:
# 下载JDK 8的tar.gz版本到/opt目录并解压 cd /opt tar -zxvf jdk-8u202-linux-x64.tar.gz mv jdk1.8.0_202 /usr/local/java然后配置环境变量。编辑/etc/profile文件,在文件末尾加入:
export JAVA_HOME=/usr/local/java export PATH=$JAVA_HOME/bin:$PATH export CLASSPATH=.:$JAVA_HOME/lib/dt.jar:$JAVA_HOME/lib/tools.jar很多新教程已经不写CLASSPATH了,但对老SSM项目来说,加上它避免一些由类加载路径引发的奇怪异常。修改完让配置生效,用source /etc/profile加载,然后验证:
java -version javac -version如果这两个命令能正常输出版本信息,JDK这步就过了。这里有一个特别常见的坑:服务器上可能预装了旧版本OpenJDK,或者你的JAVA_HOME指错了位置,导致Tomcat启动时用的是系统自带的Java而不是你装的。排查方法是运行echo $JAVA_HOME看一下值对不对,再跑which java看它指向的路径是不是/usr/local/java/bin/java。
3.2 安装Tomcat:目录结构和启动逻辑
Tomcat的安装比JDK还简单,下载tar.gz包解压即可。我把Tomcat统一放在/usr/local/tomcat,目录内部有bin、conf、lib、logs、webapps、work这六个关键目录。bin目录放启动和停止脚本,conf目录放server.xml等配置文件,logs目录是日志的家,webapps目录是部署War包的地方,work目录存放JSP编译后的临时文件。
cd /opt tar -zxvf apache-tomcat-9.0.85.tar.gz mv apache-tomcat-9.0.85 /usr/local/tomcat chmod +x /usr/local/tomcat/bin/*.sh启动之前,建议先确认一下环境变量JAVA_HOME是否已生效。Tomcat的启动脚本catalina.sh会读取JAVA_HOME来定位JDK,如果为空,启动脚本会直接报错。第一次启动使用的是:
/usr/local/tomcat/bin/startup.sh启动后同时运行一个命令验证进程是否真的起来了:
ps -ef | grep tomcat如果进程存在,再查看日志文件/usr/local/tomcat/logs/catalina.out,确认没有异常。Tomcat默认监听8080端口,此时在服务器本地可以先用curl命令测试:
curl http://localhost:8080如果返回HTML内容,说明Tomcat装好了。很多人在这一步卡住,可能就是解压后忘了给脚本加执行权限,或者JAVA_HOME没配置对,导致startup.sh提示找不到命令。
3.3 安装MySQL:初始化、库表创建和远程访问授权
MySQL的安装方式根据自己的系统和喜好来,Ubuntu可以用apt install mysql-server,CentOS可以用yum install mysql-server。安装完成之后,第一步是启动服务并查看状态:
systemctl start mysqld systemctl enable mysqld systemctl status mysqldMySQL 5.7及以上版本,在安装时会生成一个临时密码,写在日志文件里,你需要先找到这个密码再改掉。搜索方式:
grep 'temporary password' /var/log/mysqld.log如果版本没有临时密码机制,可以直接用mysql命令进入。进入MySQL之后,先创建业务库和业务账号。这里我给一个标准参考:
CREATE DATABASE ssm_project DEFAULT CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci; CREATE USER 'ssm_user'@'%' IDENTIFIED BY '这里填强密码'; GRANT ALL PRIVILEGES ON ssm_project.* TO 'ssm_user'@'%'; FLUSH PRIVILEGES;这里的'%'表示允许任意主机远程连接。在实际生产环境,从安全角度考虑可以限制为应用服务器内网IP,但如果你的项目暂时只有一台服务器,而且Tomcat和MySQL在同一台机器上,可以考虑只允许localhost登录。不过为了调试方便,一般还是先放开%,同时配合防火墙和数据库IP白名单来约束。
为什么这个环节容易出问题?因为默认情况下MySQL只监听127.0.0.1这个地址,外部其他机器就连不上。你需要在配置文件my.cnf里检查bind-address这一项,如果设成了127.0.0.1,就把它注释掉或者改成0.0.0.0。改完重启MySQL服务才能生效。
远程连接的另一个大坑是MySQL 8的认证插件。如果在授权时用了caching_sha2_password,老版本的Java驱动会连不上。我会建议同时更新项目里的数据库驱动到8.0+,并在JDBC连接串上追加两个参数:
jdbc:mysql://127.0.0.1:3306/ssm_project?useSSL=false&allowPublicKeyRetrieval=true&characterEncoding=utf8其中allowPublicKeyRetrieval=true专门解决数据库驱动在连接MySQL 8时无法获取公钥的报错,useSSL=false则跳过了本地SSL认证环节。这两个参数在本地开发时可能不需要,但到了MySQL 8环境经常会遇到,直接加上是为了稳妥。
3.4 防火墙与安全组:部署完连不上VMware的常见原因
项目部署完,浏览器访问不了,很多人第一反应是项目代码有问题,或者Tomcat没启动。但我见过的真实案例里,有一大半其实是防火墙或者云服务器安全组没放行对应端口。
这里要区分两套东西。第一套是服务器操作系统自带的防火墙。如果用的是firewalld,用下面的命令放行8080和3306端口:
firewall-cmd --permanent --add-port=8080/tcp firewall-cmd --permanent --add-port=3306/tcp firewall-cmd --reload如果用的是ufw,则是:
ufw allow 8080/tcp ufw allow 3306/tcp第二套是云服务器厂商控制台里的安全组规则。这个要登录到云厂商的网页控制台去操作,把8080、3306、80等端口加进入方向的放行列表。不同厂商的安全组界面不一样,但逻辑相同:默认都是“拒绝所有入站,仅放行22端口”,你需要手动添加新规则。这一步是最容易忽略的,很多人捣鼓半天防火墙,结果发现安全组压根没放行。
排查这类问题时,建议先用一台外部机器做telnet测试:
telnet 服务器公网IP 8080如果连接成功说明端口通了;如果超时,多半是安全组或防火墙没放行。别在本地反复折腾Tomcat配置,方向错了纯属浪费时间。
4. 项目部署与验证:从War包上传到全链路跑通
环境装好了,配置做好了,接下来就是真正把项目放上去。这个阶段虽然命令不多,但每个动作背后都有明确的目的,特别是重部署时,只有理解了Tomcat的目录和缓存逻辑,才不会出现“改了代码没生效”的诡异问题。
4.1 上传War包:scp命令和安全校验
上传War包最直接的方式是用scp命令,在本地终端执行:
scp target/ssm_project.war root@服务器公网IP:/usr/local/tomcat/webapps/如果你用的是密钥登录,命令里不需要输密码;如果用的是密码登录,会提示输入服务器root密码。上传完成后建议先检查文件大小,避免传输出错导致War包不完整:
ls -lh /usr/local/tomcat/webapps/ssm_project.war一个完整的SSM项目War包,大小通常在几十兆到上百兆之间。如果文件只有几个KB,你得怀疑是不是本地打包出了问题或者上传中断了。
4.2 选择部署方式:自动解压和Context配置
Tomcat部署War包有两种常用方式。最简单的是把War包直接放进webapps目录,Tomcat启动时会自动解压成同名目录,然后通过http://服务器IP:8080/ssm_project访问。这种方式适合日常部署,重部署时只需覆盖War包并重启。
第二种方式是在conf/server.xml里配置Context,显式指定项目路径和物理目录。比如:
<Host name="localhost" appBase="webapps" unpackWARs="true" autoDeploy="true"> <Context path="/ssm" docBase="/data/app/ssm_project" reloadable="true" /> </Host>docBase指向War包解压后的目录或者War包本身均可。这种方式比较适合项目路径和Webapps目录不在一起,或者想把项目放到独立磁盘上的场景。但对于单机单项目,第一种方式已经足够。我平时默认用第一种,只在需要把不同目录下的多个应用都挂在同一个Tomcat时才用Context。
这里有个很容易踩的细节:Tomcat在自动解压时会创建同名的文件夹,如果这个文件夹已经存在且内容不是最新的,Tomcat会直接使用旧目录,导致你上传了新War包却跑的是旧代码。遇到这种情况,先删掉旧的解压目录再重启Tomcat。
rm -rf /usr/local/tomcat/webapps/ssm_project4.3 修改Tomcat配置:端口、字符编码和内存参数
Tomcat启动之前,有三类配置建议先改好,不然项目跑起来后会发现各种别扭。
第一是端口。如果服务器上只有这一个项目,用8080没问题。但如果要把80端口对外开放,简化访问地址,就需要修改conf/server.xml里Connector的port属性。不过我不建议一上来就改端口,先用默认8080把项目调通了,再加Nginx做转发,这样每一层的问题都清清楚楚。
第二是字符编码。Tomcat 8.5及以上版本默认URI编码已经是UTF-8,不需要额外设置。但如果你用的是老版本Tomcat,或者项目里有复杂的QueryString参数,建议在server.xml的Connector标签里加上:
URIEncoding="UTF-8"否则中文字符在URL请求里会变成乱码,接口返回的数据也可能因此对不上。
第三是JVM内存。Tomcat启动时默认的堆内存很可能不适合SSM项目,特别是在访问量稍高或者项目依赖较多的时候,会频繁触发Full GC甚至OOM。推荐在bin/catalina.sh或者bin/setenv.sh里显式指定JVM参数:
JAVA_OPTS="-Xms512m -Xmx1024m -XX:MetaspaceSize=128m -XX:MaxMetaspaceSize=256m -Dfile.encoding=UTF-8"-Xms是初始堆大小,-Xmx是最大堆大小,MetaspaceSize是元空间初始大小。设置时有个原则:物理内存4G的服务器,JVM堆内存不要超过2G,要留内存给操作系统、MySQL和Tomcat自身,否则系统会开始使用Swap交换分区,整个服务性能急剧下降。我这里给的是保守值,实际可以按项目情况上调到1G对2G,但别盲目贪大。
4.4 启动项目与日志观察
配置改完之后,就可以启动了。启动命令还是:
/usr/local/tomcat/bin/startup.sh这里注意:执行startup.sh之后终端会显示Tomcat started提示,但项目是否真正启动成功,要等几秒钟才能确定。SSM项目启动时要初始化Spring容器、创建数据源、执行MyBatis映射,通常需要5到15秒。可以通过查看catalina.out确认启动进度。
tail -f /usr/local/tomcat/logs/catalina.out你会看到很多日志。关键看最后有没有下面这些标志:
Spring WebApplicationInitializers detected on classpath,说明Spring容器开始初始化Root WebApplicationContext: initialization completed in xxx ms,说明Spring根容器创建完成Deploying web application archive ...和Deployment of web application archive ... has finished,说明Tomcat部署完成
如果日志里出现Exception、Error字样,项目多半没起来。此时需要往下翻,找到第一个异常堆栈。看堆栈信息时不要只看第一行,要往下面找Caused by,那才是真正的根因。
项目启动成功的判断标准,不是Tomcat进程在跑,而是你通过HTTP请求能拿到业务响应。先在服务器本地验证:
curl http://localhost:8080/ssm_project/如果返回了HTML页面内容,说明项目本身没问题。然后再通过浏览器访问http://公网IP:8080/ssm_project/,这个能通,整个部署链路才算完成。
4.5 从IP访问到域名和HTTP简化
项目通过IP加端口访问成功后,还有一个可选的优化:给项目配一个域名,并把访问端口从8080改成80。改端口有两种方式,一种是在server.xml里直接改,另一种是引入Nginx做反向代理。我更推荐后者,因为Nginx可以做静态资源缓存、请求日志、SSL证书终止,以后扩展也更灵活。
用Nginx时配置很简单:
server { listen 80; server_name www.example.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; } }把域名解析到服务器IP,Nginx监听80端口,所有请求都转发给本机8080的Tomcat。这样访问者只需输入域名或IP就能访问,无需记住端口号。域名解析生效需要一些时间,如果你只是临时测试,可以直接用IP访问。
5. 常见问题与排查技巧:部署过程中踩过的坑都在这了
部署这种事,说得再详细,实际操作中还是会出现各种意外。我把这几年帮别人排查和自己在部署过程中遇到的高频问题整理出来,每个问题都给出可以照抄的排查步骤。日志看不懂、404解决不了、数据库连不上、内存爆掉,这类问题看这一节就够了。
5.1 部署后访问出现404:先分清是Tomcat的问题还是项目的问题
404是最常见的错误,但很多人一看到404就去改代码,方向错了。首先要分清楚你是访问Tomcat根路径就404,还是访问你的项目路径才404。
在服务器本地执行curl http://localhost:8080,如果返回的是Tomcat默认首页的HTML内容,说明Tomcat正常。如果这一步就404,可能是webapps目录下缺少ROOT目录,或者Tomcat部署异常。此时再执行curl http://localhost:8080/ssm_project,如果返回404,则说明项目没有被正确部署。
排查项目没部署成功,先回车看logs/catalina.out里有没有异常的部署日志。日志里如果出现Deployment of web application archive ... has finished,但访问仍然404,那大概率是项目的上下文路径不对。比如你的War包名是ssm_project,但项目内部配置的context path是/ssm,两者不一致,就会404。统一它们的方法是别去改代码,而是调整访问URL,让它跟上War包名称一致,重部署的时候也保持名称稳定。
另外还有一个隐蔽原因:项目中配置了SpringMVC的路由,默认首页不是根路径,而是/index或者/login。所以访问/的时候可能确实没有可用路由,此时在浏览器里打开/ssm_project/login试试,如果页面正常,说明项目其实是好的,只是你没有访问对的入口。
5.2 数据库连接报错:一堆密码错误和SSL问题的速诊
数据库连接报错,几乎每个SSM项目部署都会遇到。常见报错有以下几类,我把它们和解决思路放在一起说。
第一类:Access denied for user。数据库账号密码不对,或者该账号没有被授权从当前主机连接。解决思路是在MySQL里重新授权账号,并把授权范围定到'%',或者确认连接串里的密码真的和生产库密码一致。这类问题要特别注意:很多人在本地数据库上改了密码,但项目配置文件里还是旧密码,打包时又没注意多环境配置,于是带到服务器上。
第二类:Host 'xxx' is not allowed to connect to this MySQL server。这是MySQL没有授权这个IP来源。解决思路就是执行之前提到的授权语句,并在my.cnf里把bind-address注释掉或改成0.0.0.0。
第三类:Public Key Retrieval is not allowed。这是MySQL 8和新版驱动之间的问题,解决方式很直接:JDBC连接串加上allowPublicKeyRetrieval=true和useSSL=false。
第四类:Communications link failure。原因很多,MySQL服务没启动、3306端口被防火墙拦截、或者连接串里的IP和端口写错。逐项排查顺序是:服务器上systemctl status mysqld确认服务在跑,然后mysql -h 127.0.0.1 -P 3306测试本机连接,最后检查防火墙和安全组是否放行3306。
5.3 内存溢出和不定期假死:Tomcat崩溃的三板斧
Tomcat运行几天后内存占满,进程还在但请求都不响应,这是另一种让人头疼的情况。基本原因都在JVM内存参数上。如果你启动Tomcat时没有显式设置-Xmx,JVM默认只取物理内存的四分之一,而且元空间也会因为Spring、MyBatis的类加载量过大而频繁扩张。
处理方式首先是查看崩溃前留下的日志。OutOfMemoryError会直接打印在catalina.out里,搜索关键字就能定位问题发生在哪个线程。然后用jstat命令观察JVM运行状态:
jstat -gcutil <Tomcat的Java进程PID> 1000 10如果老年代使用率长期在90%以上,说明堆内存确实不够,或者代码里有内存泄漏。堆内存不够时调大-Xmx值,但注意不要超过物理内存的一半。如果是代码泄漏导致的,调大内存只是拖延时间,更根本的还是要查业务代码里的集合缓存、数据库连接是否正常释放。
还有一种“假死”是线程池被占满,表现是Tomcat进程活着,但新请求全部排队超时。这种情况多在接口响应过慢或者有阻塞操作时发生,和内存关系不大。排查时在服务器上执行jstack导出线程快照,看有没有线程长时间卡在某个锁上,就能还原现场。
5.4 日志文件不输出:部署后最容易被忽视的盲区
项目启动成功,接口也通,但服务的日志里什么都没有,或者日志写到了奇怪的地方。这种问题属于“慢性病”,不影响上线但不方便维护。原因主要有两个:一是log4j或logback配置里写的是Windows路径;二是配置文件没有跟随打包进入生产环境。
排查思路很简单:先看项目里日志配置文件是log4j.properties还是logback.xml,找到里面日志路径的配置项。如果是D:/logs/app.log这种绝对路径,改成Linux路径比如/var/log/ssm_project/,并在系统里提前创建目录。这里有个细节:路径别用相对路径和绝对路径混杂,最好统一绝对路径,避免Tomcat在不同的启动位置下路径解析不一致。
还有一个常见问题是多个应用共用一个Tomcat时,日志会混合写进同一个catalina.out。解决办法是在logback配置里单独给每个应用设置文件名,并开启按天滚动。这样后续排查问题只需要盯着自己项目的日志文件看,效率高得多。
5.5 日志排查速查表:按图索骥找根因
为了方便你部署时不慌,我整理了一张表格,覆盖最常见的几种报错和对应的排查方向。
| 报错或现象 | 可能原因 | 排查方向 |
|---|---|---|
| 访问8080无响应 | 防火墙/安全组未放行端口 | 查安全组和firewall-cmd |
| Tomcat启动报错找不到JAVA_HOME | JDK环境变量没配置 | 检查/etc/profile |
| UnsupportedClassVersionError | JDK版本过低,不兼容编译版本 | 换JDK 8或重新编译 |
| Access denied for user | 数据库账号密码或授权不对 | 重新授权,核对密码 |
| Public Key Retrieval is not allowed | MySQL 8认证插件问题 | JDBC URL加allowPublicKeyRetrieval=true |
| Communications link failure | MySQL未启动或3306不通 | 查MySQL状态、防火墙 |
| OutOfMemoryError: Java heap space | JVM堆内存过小或泄漏 | 调大-Xmx或排查泄漏 |
| 页面中文乱码 | 字符编码不一致 | 设URIEncoding=UTF-8和-Dfile.encoding=UTF-8 |
| 上传新War包没生效 | 旧的解压目录未被清理 | 删除webapps下旧目录再重启 |
| Spring容器初始化报错后退出 | 配置或依赖缺失 | 看catalina.out中Caused by |
这张表不能覆盖所有情况,但能帮你在遇到报错时先过滤掉最常规的坑。拿到一条报错信息后,先别急着全文搜,先把报错里的关键名词拆出来,比如SecurityException、ClassNotFoundException、SocketException,再对应到上面的方向上去查,效率会高很多。
部署这件事,说到底就是一个把“本机能跑”变成“服务器上能跑”的过程。我第一次手动部署SSM项目时也折腾了一整晚,后来发现大部分时间都耗在防火墙、版本兼容和配置路径上。有过这次经历后,我每次部署都会先做版本清单、先改生产配置、先放行端口,再真正启动项目。按照这个顺序来,整个流程其实很顺。我的个人体会是:手动部署一次SSM项目,比看十篇部署教程都管用,因为在踩坑和填坑之间,你会真正理解每一个组件在整个链条中的位置。希望你也能在自己的服务器上,一步步把项目跑起来。