news 2026/10/5 13:16:07

Java 项目实战: 外卖平台优化-Nginx部署静态资源与location匹配

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
Java 项目实战: 外卖平台优化-Nginx部署静态资源与location匹配

接入 Nginx: 静态资源部署与 location 路径匹配

纲要

「部署静态资源」,是Nginx三大应用的第一个:

  • 什么是静态资源:服务端真实存在、可直接返回的文件
  • 为什么不用Tomcat部署静态资源:sendfile零拷贝、epoll模型、无JVM开销
  • 部署方式:把文件复制到html目录即可
  • 逐条解释配置:listen、server_name、location、root、index
  • 实操演进:部署hello.html→ 设为默认首页 → 改端口 → 多server共存
  • 真实踩坑:漏分号导致reload失败、浏览器缓存导致改了没生效

否

是

准备 hello.html

cp 到 /usr/local/nginx/html/

浏览器访问
http://192.168.138.100/hello.html

想让它成为首页?

完成

修改 index 指令为 hello.html

nginx -t 检查

nginx -s reload

访问 http://192.168.138.100
直接显示 hello.html

一、什么是静态资源

静态资源指在服务端真实存在、内容固定、可以直接返回给客户端的文件,不需要任何计算或数据库查询:

类型扩展名说明
页面.html/.htm静态HTML
样式.css层叠样式表
脚本.jsJavaScript
图片.jpg/.png/.gif/.svg图片资源
字体.woff/.woff2/.ttf字体文件
音视频.mp4/.mp3媒体文件

外卖平台里的具体例子:移动端H5页面、后台管理的Vue打包产物、菜品图片、商家logo。

与之相对的是动态资源:需要程序运算生成的响应,比如Spring Boot返回的JSON订单列表——每次请求结果都可能不同,必须交给应用服务器。

二、为什么用 Nginx 而不是 Tomcat

Tomcat也能部署静态资源(放webapps下即可),但生产上几乎不会这么做。差距来自三点:

sendfile零拷贝

Tomcat读文件发送要经历 4 次数据搬运、2 次上下文切换:

磁盘 → 内核缓冲区 → 用户缓冲区 → socket 缓冲区 → 网卡

Nginx开启sendfile on后,数据在内核态直接从文件描述符送到socket:

磁盘 → 内核缓冲区 → 网卡

省掉两次CPU拷贝和全部用户态/内核态切换。传大文件(菜品图片、视频)时差距尤其明显。

并发模型

Tomcat一请求一线程,并发受限于maxThreads(默认 200)。静态资源请求虽然处理快,但会长时间占住线程(尤其是慢速客户端)。Nginx的epoll单worker就能维持数万连接,慢客户端几乎不消耗额外资源。

无 JVM 开销

Nginx是C写的,常驻内存几MB;Tomcat光JVM堆就要几百MB起步。把宝贵的JVM资源留给业务逻辑是常识。

典型架构:Nginx在最外层,命中静态资源的直接返回,动态请求才proxy_pass给Tomcat。

/api/** 动态请求

/*.html、/*.js、/*.png 静态请求

客户端

Nginx :80

Spring Boot :8080

本地 html 目录
直接 sendfile 返回

MySQL 主从

三、部署实操

第一步:准备静态页面

课程提供了一个hello.html,内容如下:

<!DOCTYPEhtml><htmllang="zh-CN"><head><metacharset="UTF-8"><title>hello Nginx</title></head><body><h2>hello Nginx ...</h2></body></html>

把它放到/根目录(也可以放在任意位置,只要cp时写对路径)。

第二步:复制到 html 目录

cp/hello.html /usr/local/nginx/html/

验证:

cd/usr/local/nginx/htmlls# 50x.html hello.html index.html

这就是全部部署动作——因为默认配置里root html;已经指向这个目录。

第三步:启动并访问

nginxps-ef|grepnginx

浏览器访问:

http://192.168.138.100/hello.html

看到hello Nginx ...即部署成功。

访问http://192.168.138.100(不带文件名)则显示默认的Welcome to nginx!,因为默认首页是index.html。

四、关键配置逐条解释

打开配置文件:

vim/usr/local/nginx/conf/nginx.conf

核心部分:

server { listen 80; # 监听端口 server_name localhost; # 服务器名称(域名) location / { # 匹配客户端请求 URI root html; # 指定静态资源根目录 index index.html index.htm; # 指定默认首页 } }

listen—— 监听端口

listen 80;

Nginx在哪个端口等待连接。常见写法:

写法含义
listen 80;监听所有网卡的80端口
listen 81;监听81端口
listen 127.0.0.1:80;只监听本机回环,外部访问不到
listen 443 ssl;HTTPS
listen 80 default_server;作为该端口的默认虚拟主机

改成81后,原来的80就访问不到了。这是课程里演示过的一步:改端口 →reload→ 用80访问失败 → 用81访问成功。

server_name—— 服务器名称

server_name localhost;

学习阶段没有域名就写localhost;生产环境写真实域名,如server_name www.example.com;。

它的真正作用在于多个server监听同一端口时的分流:Nginx用请求头里的Host值去匹配各server的server_name,匹配到哪个就交给哪个处理。

server { listen 80; server_name admin.example.com; # 后台管理 location / { root html/admin; } } server { listen 80; server_name m.example.com; # 移动端 location / { root html/mobile; } }

这样一个80端口就能托管两个站点。支持通配符(*.example.com)和正则(~^www\d+\.example\.com$)。

location /—— 路径匹配

location / { ... }

location后面跟的是匹配规则,/表示匹配所有请求。

常见匹配方式(优先级从高到低):

写法类型含义
location = /path精确匹配只匹配完全相同的URI
location ^~ /static/前缀匹配(优先)匹配前缀,且不再检查正则
location ~ \.php$正则匹配(区分大小写)按正则匹配
location ~* \.png$正则匹配(不区分大小写)按正则匹配
location /通用前缀匹配兜底,优先级最低

外卖平台的典型配置:

# 精确匹配首页 location = / { root html; index index.html; } # 静态资源走本地,设置长缓存 location ^~ /static/ { root html; expires 7d; } # 图片单独配置 location ~* \.(jpg|jpeg|png|gif|ico|svg)$ { root html; expires 30d; access_log off; } # 动态请求转发后端 location /api/ { proxy_pass http://192.168.138.101:8080; }

root—— 静态资源根目录

root html;

html是相对路径,基准是Nginx安装目录/usr/local/nginx,所以实际路径是/usr/local/nginx/html。

也可以写绝对路径,语义更清晰:

root /usr/local/nginx/html;

请求http://192.168.138.100/hello.html的解析过程:

URI = /hello.html root = /usr/local/nginx/html 最终文件路径 = /usr/local/nginx/html + /hello.html = /usr/local/nginx/html/hello.html

index—— 默认首页

index index.html index.htm;

当请求的URI以/结尾(即访问目录而非具体文件)时,Nginx依次尝试这些文件。

  • 找到第一个存在的就返回
  • 都不存在且未开启目录列表,返回403 Forbidden(目录不可访问)
  • 路径本身不存在,返回404 Not Found

多个值用空格分隔,不是逗号:

index index.html index.htm index.php;

五、把 hello.html 设为默认首页

修改配置

vim/usr/local/nginx/conf/nginx.conf

找到index那一行,注释掉原配置,另起一行:

location / { root html; # index index.html index.htm; index hello.html; }

保存退出(:wq)。

踩坑:漏写分号

如果写成:

index hello.html ← 少了分号 }

nginx -s reload会报:

nginx: [emerg] unexpected "}" in /usr/local/nginx/conf/nginx.conf:46

注意报错行号 46 是花括号所在行,真正的问题在上一行。Nginx把下一行的}当成了index指令的延续,直到遇见{或不匹配的括号才报错。

修正:

vim/usr/local/nginx/conf/nginx.conf :set nu# 显示行号# 找到对应行,补上分号

这个坑的教训:改配置后先nginx -t,它会直接报directive "index" is not terminated by ";",比reload的报错更准确。

重新加载

nginx-t# nginx: the configuration file ... syntax is ok# nginx: configuration file ... test is successfulnginx-sreload

验证

访问http://192.168.138.100(不带文件名),现在直接显示hello Nginx ...。

如果还是显示旧页面,是浏览器缓存。做一次强制刷新:

Ctrl + F5 (Windows) Cmd + Shift + R (Mac)

或者在开发者工具Network面板勾选Disable cache。这是前端调试时的高频困惑——配置明明生效了,但浏览器拿的是本地缓存。

六、修改监听端口

server { listen 81; # 从 80 改为 81 server_name localhost; location / { root html; index hello.html; } }
nginx-tnginx-sreload

验证:

  • http://192.168.138.100→ 访问不到(80已无监听)
  • http://192.168.138.100:81→ 正常访问

注意:非80端口必须在URL里显式写出。同时别忘了防火墙放行:

firewall-cmd--zone=public --add-port=81/tcp--permanentfirewall-cmd--reload

七、配置多个 server

一个http块里可以有多个server,同时监听不同端口:

http { include mime.types; default_type application/octet-stream; sendfile on; # 站点一:80 端口,默认首页 server { listen 80; server_name localhost; location / { root html; index hello.html; } } # 站点二:81 端口,原默认页面 server { listen 81; server_name localhost; location / { root html; index index.html index.htm; } } }
nginx-tnginx-sreload

现在80和81都能访问,且返回不同页面。

也可以让两个server监听同一端口,用server_name区分(需要配置hosts或真实域名才能验证):

server { listen 80; server_name admin.reggie.com; location / { root /usr/local/nginx/html/admin; index index.html; } } server { listen 80; server_name m.reggie.com; location / { root /usr/local/nginx/html/mobile; index index.html; } }

八、外卖平台的前端部署结构

把这套思路套到外卖平台上,最终的静态资源目录会是:

/usr/local/nginx/html/ ├── backend/ # 后台管理系统(Vue 打包产物) │ ├── index.html │ ├── static/ │ │ ├── css/ │ │ ├── js/ │ │ └── fonts/ │ └── favicon.ico ├── front/ # 移动端 H5 │ ├── index.html │ └── static/ └── img/ # 上传的菜品图片 ├── dish-xxxx.jpg └── setmeal-yyyy.png

对应配置:

server { listen 80; server_name localhost; # 后台管理系统 location /backend/ { alias /usr/local/nginx/html/backend/; index index.html; try_files $uri $uri/ /backend/index.html; # 支持 history 路由 } # 移动端 location /front/ { alias /usr/local/nginx/html/front/; index index.html; try_files $uri $uri/ /front/index.html; } # 上传的图片 location /img/ { alias /usr/local/nginx/html/img/; expires 30d; access_log off; } # 动态请求转发后端 location /api/ { proxy_pass http://192.168.138.101:8080/; } }

三个要点:

  • try_files是SPA必需的:Vue Router用history模式时,/backend/dish/list这个URI在磁盘上不存在对应文件,必须回退到index.html交给前端路由处理,否则刷新页面会 404
  • alias比root更直观:/backend/→/usr/local/nginx/html/backend/,不需要理解拼接规则
  • 图片配expires+access_log off:长缓存头减少重复请求,关掉访问日志减少磁盘IO

九、常见问题

现象原因解决
访问返回 404文件不在root指定的目录检查文件路径与root配置
访问返回 403目录不可读,或首页文件都不存在检查文件权限、index配置
CSS/JS加载后页面样式全无未include mime.types或MIME类型不对检查http块里的include
改了配置没生效忘记reloadnginx -s reload
reload后仍是旧页面浏览器缓存Ctrl + F5强制刷新
reload报unexpected "}"上一行漏了分号vim里:set nu定位,补分号
改了端口访问不到防火墙未放行新端口firewall-cmd --add-port=81/tcp --permanent
图片上传报 413client_max_body_size默认1m在http块调大,如client_max_body_size 100m;

API 速览

指令作用
listen <port>监听端口
server_name <name>虚拟主机域名,用于同端口多站点分流
location [= | ^~ | ~ | ~*] <uri>路径匹配块,符号决定匹配方式与优先级
root <path>静态资源根目录,最终路径 =root+ 完整URI
alias <path>路径别名,最终路径 =alias+ 去掉location前缀的剩余部分
index <file>...默认首页,空格分隔多个,依次查找
try_files <file>... <fallback>依次尝试文件,都不存在则回退(如/index.html)
expires <time>设置响应缓存头Cache-Control/Expires
access_log off关闭该location的访问日志
client_max_body_size <size>允许的最大请求体,默认1m
cp <src> <dest>复制文件到html目录完成部署
vim +:set nu编辑时显示行号,便于定位报错行

官方文档

  • Nginx静态资源服务:https://nginx.org/en/docs/http/ngx_http_core_module.html#location
  • NginxBeginner's Guide静态内容:https://nginx.org/en/docs/beginners_guide.html#static
  • Nginxroot指令:https://nginx.org/en/docs/http/ngx_http_core_module.html#root
  • Nginxalias指令:https://nginx.org/en/docs/http/ngx_http_core_module.html#alias
  • Nginxindex指令:https://nginx.org/en/docs/http/ngx_http_index_module.html
  • Nginxtry_files指令:https://nginx.org/en/docs/http/ngx_http_core_module.html#try_files

总结

部署静态资源就是把文件复制到html目录。默认配置的root html;已经指向/usr/local/nginx/html,location /匹配所有请求,index index.html指定默认首页。

Nginx处理静态资源强于Tomcat,核心是sendfile零拷贝 +epoll高并发 + 无JVM开销。生产上静态资源一律走Nginx,只把动态请求转发给应用服务器。

五个关键指令要分清:listen管端口、server_name管域名分流、location管路径匹配、root/alias管文件根路径、index管默认首页。其中root与alias的拼接规则不同——root拼完整URI,alias替换掉location前缀,这是静态资源 404 的头号原因。

改配置的完整流程:vim改 →nginx -t检查 →nginx -s reload生效 → 浏览器Ctrl + F5强制刷新验证。四步缺一不可,尤其是最后一步——浏览器缓存造成的"改了没生效"是最高频的假故障。

漏分号的报错行号会滞后一行。看到unexpected "}"就往上翻一行找分号;用vim的:set nu能快速定位。先nginx -t再reload能拿到更准确的错误信息(directive is not terminated by ";")。

SPA部署必须加try_files。否则Vue的history路由在刷新时会出现 404,因为服务端磁盘上不存在/backend/dish/list这样的文件。

下一篇讲Nginx最重要的能力:反向代理——把动态请求转发给后端的Spring Boot,让前后端路径统一在同一个域名和端口下。

版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/10/5 13:15:13

用Python批量生成画展展签:一间美术教室的数字化尝试

画展筹备里最琐碎的活是展签&#xff1a;两百多张作品&#xff0c;每张要写姓名、班级、作品名、材料。这篇记录我用 Python 把展签和作品集做成批量生成的过程&#xff0c;从 CSV 数据整理到 PDF 排版&#xff0c;全部本地完成。关键词&#xff1a;Python、reportlab、qrcode、…

作者头像 李华
网站建设 2026/10/5 13:13:26

首字响应低至 280ms+动态慢思考!MiniMax M3.1-Flash-Preview 突袭 MCode:日常编程智能体迎来平民级“超跑小钢炮”

核心震撼导读&#xff1a;如果你以为顶级 AI 编程助手的进化终点依然是死磕数千亿巨无霸参数、让开发者在终端前焦急等待数秒首字响应&#xff0c;那么刚刚在 MiniMax Code (MCode) 爆发的这场“极速轻量突袭”&#xff0c;将彻底颠覆你对全栈编程智能体&#xff08;Coding Age…

作者头像 李华
网站建设 2026/10/5 13:06:03

Large Language Models‘ Internal Perception of Symbolic Music

文章主要内容和创新点 主要内容 本文研究了大型语言模型(LLMs)对符号音乐的隐式建模能力。研究通过文本提示(描述音乐类型和风格的组合)让LLM生成符号音乐数据(MIDI文件),并通过识别和生成任务评估其效用: 数据集生成:使用GPT-4生成了包含13个音乐类型(源自TOP-MAG…

作者头像 李华
网站建设 2026/10/5 13:05:42

描述文件移除之后,设备为什么还在管理域里

先给结论&#xff1a;因为「受监督」和「装了描述文件」是两件独立的事。描述文件是设备侧的一份配置&#xff0c;可以删&#xff1b;受监督是设备的一个状态标志&#xff0c;由注册方式决定&#xff0c;删文件删不掉它。所以同一个移除动作&#xff0c;在不同注册方式的设备上…

作者头像 李华
网站建设 2026/10/5 12:57:53

机票预订系统详细设计落地的5大工程断点

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

作者头像 李华
网站建设 2026/10/5 12:56:07

Qt下载与安装避坑指南:版本选择、镜像配置与环境搭建

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

作者头像 李华