1. 先想清楚:若伊这套框架,到底该怎么部署才合理
很多朋友拿到若伊(RuoYi)框架的第一反应是往服务器上扔代码,然后问我:“我该用Tomcat还是Tomcat加Nginx?”说实话,这个问题没有标准答案,取决于你是想在本地跑通演示,还是让它在生产环境稳定扛流量。我自己的结论很明确:单机演示用纯Tomcat足够,但凡要上线、要给人用,老老实实走Tomcat+Nginx。
这套框架脱胎于Spring Boot,默认开发阶段我们用mvn spring-boot:run就能起来,为什么还要谈部署方式?因为框架里既有后端Java代码,又有前端Vue打包出来的静态资源。部署的本质问题是:谁负责跑Java逻辑,谁负责喂静态文件,谁对外暴露统一入口。这些角色分得越清楚,后面扩容、排查问题就越轻松。
如果走纯Tomcat部署,一般有两种做法。一种是改造成war包丢进webapps目录,让Tomcat的Servlet容器接管一切;另一种是仍旧打成jar包,但把前端dist里的静态文件拷到src/main/resources/static下重新打包,让Spring Boot内置的Tomcat一把梭。这两种方案都能让若伊跑起来,但都有一个共同短板:静态资源全部压在Tomcat的线程池上,图片稍微多几个、并发稍微上来一点,Tomcat默认的200个工作线程就容易被耗尽。
引入Nginx之后,情况完全变了。Nginx作为前置代理监听80或443端口,接管浏览器请求;纯静态文件(图片、CSS、JS)直接由Nginx处理,只有带/api、/prod-api这类动态路径的请求才转发给后端的Tomcat。这是大多数若伊生产部署的标准形态,也是本文要重点展开的主线。
为了帮你做决策,我整理了一个简单的判断表:
| 场景 | 推荐方式 | 理由 |
|---|---|---|
| 本地开发、功能演示 | mvn spring-boot:run | 开发效率最高,改完即生效 |
| 内网小规模使用,几十人 | 纯Tomcat部署war包 | 部署简单,够用就好 |
| 公网服务、有图片上传、并发过百 | Tomcat+Nginx | 静态资源分离,稳定性质变 |
| 高可用、多实例业务 | Nginx负载均衡+多Tomcat | Nginx天然支持upstream,扩容方便 |
2. 杀人的环境准备:JDK、Maven、前端构建一个都不能少
部署若伊有一个隐蔽的坎——不是项目起不来,是环境乱七八糟导致起不来。我第一次给别人部署时,先被JDK版本坑了一把,接着被前端node-sass编译失败折磨了两小时,后来发现都是环境版本匹配的问题。所以我要把这一步单独拎出来,说透。
2.1 JDK版本别凭感觉选
若伊官方很多版本基于Spring Boot 2.x,底层对Java 8的支持最稳定。如果你的代码是当前比较新的RuoYi-Vue或者RuoYi-Vue-plus这类分支,用JDK 8基本不会错;要是用到了更新的Spring Boot 3.x版本,那要求JDK 17起步。在动手之前,先打开pom.xml看<java.version>标签,按这个来装JDK,不要凭“最新版肯定好”的感觉装个JDK 21。
Linux服务器上验证JDK是否可真执行:
java -version echo $JAVA_HOME如果JAVA_HOME是空的,即使java -version能打印,很多构建工具一样会翻车。建议编辑/etc/profile:
export JAVA_HOME=/usr/local/jdk1.8.0_202 export PATH=$JAVA_HOME/bin:$PATH保存后执行source /etc/profile,再看一遍。这一步省不了,Maven打包时要靠JAVA_HOME定位编译器。
2.2 Maven打包:跳过测试是常规操作,但不是偷懒
项目根目录下执行:
mvn clean package -Dmaven.test.skip=true这里要注意,-Dmaven.test.skip=true会跳过测试代码的编译和运行,-DskipTests只是跳过运行、但仍编译测试类。对于部署场景,前者更快更省心。
国内网络环境拉Maven依赖经常卡住,建议在settings.xml里配阿里云镜像:
<mirror> <id>aliyunmaven</id> <mirrorOf>central</mirrorOf> <url>https://maven.aliyun.com/repository/public</url> </mirror>配完之后构建速度天壤之别。打完包,在ruoyi-admin/target/目录下会看到jar包或war包,这就是我们要的产物。
2.3 前端构建:别把dist拷错地方
若伊Vue版本的前端在ruoyi-ui目录下。本地或服务器上执行:
npm install npm run build:prod执行完会在ruoyi-ui/dist下生成静态文件。如果npm install卡在node-sass这类原生模块上,多半是Node版本不对——老的若伊前端对Node 14、16较友好,新版RuoYi-Vue3则建议Node 16+。node-sass编译失败的解决办法通常是:
npm uninstall node-sass npm install sass --save-dev改完再重新npm install。构建成功后,把dist整个目录记下来,后面部署时要把这里的index.html、static、favicon.ico等文件处理掉,不要让它们静静躺在服务器某个角落。
2.4 Linux侧的基础依赖
还要保证服务器上有unzip、tar、vim、lsof这类基础工具,尤其是lsof,排查端口占用离不开它。用YUM安装:
yum install -y unzip tar vim lsof如果服务器是Ubuntu系,就用apt-get install。这一步用不了半分钟,但能省掉后续很多“命令找不到”的尴尬。
3. 纯Tomcat部署war包:流程、验证与首次启动的坑
先讲清楚纯Tomcat方案,因为它的逻辑最简单,也是理解Tomcat+Nginx方案的基础。把若伊改造成war包部署,网上资料不少,但很多教程漏了关键步骤,导致启动后访问一直404。
3.1 从jar包到war包,要改三个地方
第一处,在ruoyi-admin/pom.xml里把默认打包方式改掉:
<packaging>war</packaging>第二处,因为Spring Boot内置Tomcat会和外部Tomcat冲突,得把内置容器标记为provided,最常见的写法是在spring-boot-starter-tomcat依赖上加:
<dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-tomcat</artifactId> <scope>provided</scope> </dependency>第三处,在启动类RuoYiApplication上继承SpringBootServletInitializer,并重写configure方法。这是让外部Tomcat能找到应用入口的关键:
@SpringBootApplication public class RuoYiApplication extends SpringBootServletInitializer { @Override protected SpringApplicationBuilder configure(SpringApplicationBuilder builder) { return builder.sources(RuoYiApplication.class); } public static void main(String[] args) { SpringApplication.run(RuoYiApplication.class, args); } }这三处改完,重新执行:
mvn clean package -Dmaven.test.skip=true在ruoyi-admin/target/下会生成ruoyi-admin.war。
3.2 部署到Tomcat:目录与启动
把war包上传到Tomcat的webapps目录,然后启动。以Tomcat 8.5/9为例:
cd /usr/local/tomcat/bin ./startup.shTomcat会自动解压war包,生成同名的ruoyi-admin目录。首次启动时,若伊的后端默认端口是8080,但注意它有独立的context-path,默认是/,所以理论上访问地址是:
http://服务器IP:8080/实际上你在项目配置里如果看到server.servlet.context-path被设置为/prod-api这类值,那就要靠路径区分了。部署时不调整的话,纯Tomcat方案的访问路径应该是http://IP:8080/,如果配置了context-path,那就访问http://IP:8080/prod-api/,但登录页通常是直接访问根路径,也就是说前端页面和接口路径可能不在同一个根下面,这一点后面踩坑时会专门提。
3.3 首次启动的坑:为什么页面白屏
好多人走到这一步,启动日志也刷了,页面就是白屏,检查过程发现Tomcat在跑,但index.html永远404。问题多半是前端静态资源没进war包。war包部署方案里,前端dist文件要放到ruoyi-admin/src/main/resources/static目录下,再一起打包。如果只改了后端配置、忘记把dist塞进static目录,那么后端起来了也没有可访问的前端页面。
验证方法很直接:
curl -I http://127.0.0.1:8080/如果返回200并且能看到index.html,说明静态资源位置对了。如果404,解压war包看看:
cd /usr/local/tomcat/webapps/ruoyi-admin ls WEB-INF/classes/static/static目录下没有前端文件,那就在ruoyi-admin/pom.xml加资源拷贝配置,或者干脆把dist目录直接复制到src/main/resources/static下重新打包。
3.4 纯Tomcat方案的验证检查单
启动完成后,我习惯按这个顺序来一遍:
- 进程是否存在:
ps -ef | grep tomcat - 端口是否监听:
lsof -i:8080 - 日志是否有异常:
tail -f /usr/local/tomcat/logs/catalina.out - 页面是否可访问:
curl -I http://127.0.0.1:8080 - 后端接口是否通:
curl -X POST http://127.0.0.1:8080/login -H 'Content-Type: application/json' -d '{"username":"admin","password":"..."}'
以上能过,说明纯Tomcat部署算成功了。但如果你要问生产环境能不能这么干——能,不过遇到高峰期有概率出现页面加载慢、图片卡顿。这就是我们下一节要聊的Nginx的价值。
4. Nginx只是反向代理?不,它决定了若伊的生产可用性
很多教程把Tomcat+Nginx说成“用一个Nginx反向代理Tomcat”,这个描述没错,但它等于没说,因为Nginx给若伊带来的价值远不止“转发请求”这一件事。
4.1 Tomcat的软肋:静态资源挤占线程
Tomcat是Servlet容器,它设计上是跑动态请求的。部署若伊后,一秒钟几十个请求打过来,如果每个请求都包含一堆JS、CSS、图片,Tomcat的线程池立刻被这些静态请求占满,动态接口反而排队。
你可以做个小实验:纯Tomcat部署下,用浏览器无缓存刷新页面,打开Network面板能看到vendor.js这个文件轻松超过1MB,本地加载可能只要几百毫秒,但在服务器带宽有限的情况下,这一个文件就要占用Tomcat连接线程好几秒。这段时间里其他用户登录、查询数据的请求只能等着。
Nginx处理静态文件几乎不耗Java线程,它的事件驱动模型扛静态请求非常擅长,一个worker进程可以同时处理几万个连接。把静态文件这摊事从Tomcat身上剥离,等价于把Tomcat的全部CPU和线程留给业务接口。
4.2 统一入口和端口复用的现实价值
如果服务器上只跑若伊,用8080直接对外也不是不能用。但现实是,一台服务器可能还跑着别的服务,或者同一个公网IP上有多个域名。Nginx监听80/443,按照server_name把不同域名分发到不同后端,这样就实现了“一个公网入口,多套服务分流”。
这一步对运维的价值极大。以后若伊服务升级、重启,Nginx不需要动;如果哪天要把HTTP换成HTTPS,只需要在Nginx层挂证书,后端Tomcat一行配置都不用改。
4.3 多实例部署时不加Nginx就麻烦大了
纯Tomcat部署想要做集群扩容,要么在前面加负载均衡器,要么就得让用户手动访问不同端口。有了Nginx,配置一个upstream就能在多台Tomcat之间做轮询。而且Nginx还能通过ip_hash实现简单的会话保持,解决用户频繁掉登录态的问题。
4.4 安全性层面,Nginx是一道免费的“铁闸”
Tomcat直接暴露到公网,等于把应用服务器、版本信息、端口细节全摊给扫描器。Nginx前置后,可以统一做:
- 请求体的最大尺寸限制,防止恶意上传把Tomcat磁盘打爆。
- URL访问规则过滤,拦截未授权路径。
- HTTPS终止,让内部HTTP流量不暴露在公网。
- 隐藏后端服务细节,响应头上不会暴露Tomcat版本。
所以我的建议很直接:如果你打算让若伊正式给用户用,别省掉Nginx这层。它解决的问题不是“多了几个功能”,而是“让服务在真实流量下还立得住”。
5. Nginx配置实操:server块、静态资源分离与Gzip
这一节是全文的重头戏。我将以典型的若伊部署目录和端口规划为例,给你一份可以直接抄的Nginx配置,并解释每一段配置背后的原因。
我的规划是:
- Tomcat跑在
127.0.0.1:8080,只允许本机访问。 - Nginx对外监听
80端口。 - 前端编译产物放在
/home/web/ruoyi-ui/dist。 - 若伊上传文件的访问路径是
/profile/,实际文件存储路径由后端配置指定。
5.1 先装Nginx
以CentOS/RHEL系为例,直接YUM源通常没有nginx,一般用官方源:
rpm -Uvh http://nginx.org/packages/centos/7/noarch/RPMS/nginx-release-centos-7-0.el7.ngx.noarch.rpm yum install -y nginx systemctl start nginx systemctl enable nginx国内服务器也可以直接编译安装,但需要额外装PCRE、zlib、openssl等依赖,比较费时间。对大多数场景,官方源安装足够。装完验证:
nginx -v curl -I http://127.0.0.1/看到Nginx欢迎页就说明装好了。Windows下部署则下载Windows版压缩包,解压后运行nginx.exe即可,不过请记住Windows下不要指望它跑生产级并发。
5.2 完整的server配置
这是我在实际项目里验证过的一份若伊Nginx配置:
upstream ruoyi_tomcat { server 127.0.0.1:8080 max_fails=2 fail_timeout=10s; } server { listen 80; server_name yourdomain.com; root /home/web/ruoyi-ui/dist; index index.html; client_max_body_size 50m; access_log /var/log/nginx/ruoyi-access.log main; error_log /var/log/nginx/ruoyi-error.log warn; # 前端静态资源 location / { try_files $uri $uri/ /index.html; } # 后端接口反向代理 location /prod-api/ { proxy_pass http://ruoyi_tomcat/; proxy_set_header Host $host; proxy_set_header X-Real-IP $remote_addr; proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; proxy_set_header X-Forwarded-Proto $scheme; proxy_connect_timeout 60s; proxy_read_timeout 120s; } # 上传文件访问 location /profile/ { alias /home/ruoyi/uploadPath/; } # 静态资源带指纹缓存,提升加载速度 location ~* \.(js|css|png|jpg|jpeg|gif|ico|svg|woff2?)$ { expires 7d; add_header Cache-Control "public, no-transform"; } gzip on; gzip_types text/plain text/css application/javascript application/json image/svg+xml; gzip_min_length 1k; }5.3 每一段的用意
/prod-api/这个路径是若伊前端默认的请求前缀,登录、业务接口全走这里。proxy_pass http://ruoyi_tomcat/;末尾的/非常关键,它的作用是把/prod-api前缀剥掉后转发给后端。如果后端若伊的context-path就是/prod-api,那就反过来,proxy_pass后面不加/。这里最容易出错,我的建议是先确认后端接口完整请求路径到底是什么,再决定proxy_pass末尾是否带斜杠。
try_files $uri $uri/ /index.html;这段,是Vue前端路由history模式的标配。它确保刷新页面时,Nginx不会把前端路由误判为真实文件路径,而是统一回到index.html,由前端路由接管。如果不写这一行,用户一旦在/system/user这类页面按F5,立刻404。
/profile/是若伊上传文件的统一访问前缀。比如上传头像后,后端返回的路径是/profile/avatar/2024/xx.jpg,Nginx直接把这段请求映射到磁盘目录/home/ruoyi/uploadPath/,不需要经过Tomcat。这样做的性能提升非常显著,图片请求完全不占后端线程。
client_max_body_size 50m;,这行特别重要。若伊里有Excel导入、文件上传功能,默认Nginx的client_max_body_size是1m,超过这个大小的请求会直接413。你既然要支持上传大文件,就把它调大,具体大小看你业务需要。
gzip配置能压缩前端JS和CSS,有些用户带宽一般,压缩之后vendor.js从1MB变成200多KB,首屏速度改善非常明显。
5.4 配置完如何验证
改完配置后,先测语法再重载:
nginx -t nginx -s reload然后验证三条链路:
- 浏览器访问
http://IP/,能出登录页。 - 登录后点菜单,接口走
/prod-api,能正常返回数据。 - 上传个头像,再访问返回的
/profile/xxx.jpg路径,图片能直接打开。
三条都通,说明Nginx前后端分离配置整体是对的。如果第二条失败,去看/var/log/nginx/ruoyi-error.log,多半是proxy_pass的路径问题。
5.5 SSL与双向认证的提醒
热搜词里提到“nginx 配置自签名证书 ssl 交互式方法”和“ssl 双向认证tomcat”,这里提个醒:生产环境强烈建议给Nginx层配置HTTPS证书。用Let‘s Encrypt或者自签名证书都可以,在Nginx的server块里加上:
listen 443 ssl; ssl_certificate /etc/nginx/ssl/server.crt; ssl_certificate_key /etc/nginx/ssl/server.key;如果你偏好交互式生成证书,可以用openssl交互式工具,按提示输入域名、组织信息即可。双向认证则涉及客户端证书签发与校验,配置量更大,需要时再单独展开。核心结论是:TLS这层放在Nginx做,比放在Tomcat做清爽得多,年底扫漏洞时能少操一大半心。
6. 从日志到端口:部署后最常见的故障排查手册
这部分是我自己部署若伊时反复踩过的坑,每一条都对应过一个“折腾半宿”的深夜。我把它按现象分类,方便你出问题时直接对号入座。
6.1 页面打不开:8080没监听还是防火墙没放行
先从本地看端口:
lsof -i:8080 ss -lntp | grep 8080如果有监听,再从外部访问。如果本机curl 127.0.0.1:8080有反应,但公网IP访问不了,九成是防火墙或服务器安全组拦了。在CentOS上:
firewall-cmd --zone=public --add-port=80/tcp --permanent firewall-cmd --zone=public --add-port=80/tcp --reload云服务器还要去控制台的安全组规则里把80端口开着。曾经有人把这条忘了,来回检查了一天Nginx配置,最后发现是安全组没放行,教训深刻。
6.2 404漂移三种情况
若伊部署中,404要区分三种位置:
- 访问
/返回404,是前端静态文件没放对。 - 刷新某个具体页面404,是
try_files那行没配或配错。 - 调用
/prod-api下的接口返回404,是前端请求的路径和后端context-path对不上。
逐一排查时,先用浏览器F12看Network面板,看请求发到了哪个URL,再顺着URL去Nginx配置里找对应的location规则。这个排查顺序能帮你少绕很多弯路。
6.3 Tomcat乱码:一个很容易“安慰自己”的问题
日志里中文乱码、页面响应乱码,原因可能出在三个环节:Tomcat的日志编码、系统字符集、前端页面编码。Linux下推荐把JVM编码强制设为UTF-8,在catalina.sh里追加:
JAVA_OPTS="$JAVA_OPTS -Dfile.encoding=UTF-8 -Dsun.jnu.encoding=UTF-8"同时确保系统locale不要是POSIX,执行locale看一下,如果输出里LC_ALL是空白,在/etc/profile里加上:
export LANG=en_US.UTF-8改完重启Tomcat再验证。至于Windows下开发时乱码,那是控制台代码页问题,与服务器部署无关,不用混在一起看。
6.4 502 Bad Gateway:后端没起来或起的太慢
Nginx配置没问题但所有接口都502,基本可以断定后端Tomcat没起来或刚启动还没就绪。Spring Boot项目启动初期,端口还没监听,此时请求打到Nginx就会502。解决办法有两种:一是等一会儿再试;二是调大proxy_connect_timeout和proxy_read_timeout,防止后端处理慢时Nginx过早断连。
如果Tomcat进程崩溃,去logs/catalina.out看异常栈。常见原因包括端口被占用、数据库连不上、Redis连接失败。若伊启动时强依赖MySQL和Redis,这两个服务任何一个没就绪,应用都可能启动失败或反复重试。
6.5 用户上传的图片404
图片404时,先看后端返回的路径长什么样。如果是/profile/upload/2024/xx.jpg,Nginx里又没有location /profile/的规则,那就会全部打到前端静态资源那里去,结果自然404。检查一下alias路径是不是和若伊上传配置一致。若伊配置里有:
ruoyi: profile: /home/ruoyi/uploadPath这就是上传文件的真实落盘路径,Nginx的alias必须指向它,末尾记得带/。
6.6 排查链路模版
给新手一套我常用的排查命令链:
# 1. 看Nginx进程 ps -ef | grep nginx # 2. 看Nginx日志 tail -n 50 /var/log/nginx/ruoyi-error.log # 3. 看Tomcat日志 tail -n 100 /usr/local/tomcat/logs/catalina.out # 4. 看端口 lsof -i:80 lsof -i:8080 # 5. 直接测后端 curl -I http://127.0.0.1:8080/prod-api/ # 看有没有响应这套顺序的思路是:从最外层入口逐层向内,先确认Nginx是否正常转发,再确认Tomcat是否正常处理,最后把问题锁定在某一个环节。
6.7 关于日志分析的一个建议
若伊的日志文件默认在logs目录下,按天滚动。真正排查问题时,我习惯先把catalina.out里的ERROR和Exception抓出来:
grep -E "ERROR|Exception" logs/catalina.out | tail -n 50然后看业务日志里有没有SQL异常、Redis超时、权限报错,绝大多数若伊生产问题都逃不出这几类。日志乱、问题杂的时候,先别急着翻整本,先锁定第一条异常,顺着它往下追,比什么都快。
回到最初的问题:若伊到底是纯Tomcat部署还是Tomcat+Nginx?我的答案一直是——看懂需求再选。但我个人把话放这儿:我现在不管给谁部署若伊,起步就直接上Tomcat+Nginx,省掉日后迁移带来的风险。顺便说个我的小习惯:Nginx配置里永远用include /etc/nginx/conf.d/*.conf;管理每个服务的配置,这样若伊的配置独立成一个文件,以后停掉、改动、备份都不影响服务器上其他服务。你部署过几次就会明白,这种细节才是真正能在凌晨两点救你命的东西。