找回密码
立即注册
搜索
热搜: Java Python Linux Go
发回帖 发新帖

2222

积分

0

好友

291

主题
发表于 1 小时前 | 查看: 2| 回复: 0

前后端分离与微服务架构对比图

下面是我在 Nginx 使用过程中发现的几个配置与运维问题,分享出来一起巩固一下相关知识点。

问题一:proxy_pass 末尾的斜杠 / 到底有何玄机?

先来看下面两段配置,你能看出它们的区别吗?

# 配置一
location /test {
 proxy_pass 'http://192.186.0.1:8080';
}

# 配置二
location /test {
 proxy_pass 'http://192.186.0.1:8080/';
}

仔细对比,唯一的区别在于 proxy_pass 指令后面的地址是否有结尾的斜杠 /!那么,这一个小小的斜杠会产生什么不同的效果呢?

假设我们请求的路径是 /test/file/getList,经过上面两种配置的反向代理后,实际向后端服务器发起的请求分别是:

  • http://192.186.0.1:8080/test/file/getList (配置一的结果)
  • http://192.186.0.1:8080/file/getList (配置二的结果)

是的,区别就在于是否保留了 /test 这个路径前缀。proxy_pass 后面的这个 /,其作用是去除 location 中匹配到的路径前缀

虽然这种方法很简洁,但对不熟悉 Nginx 的同学来说容易造成困惑。我更推荐下面这种写法,虽然多了一行,但意图清晰,可读性更好:

# 推荐的替代写法,清晰表明去除 /test 前缀
location /test {
 rewrite ^/test/(.*)$ /$1 break;
 proxy_pass 'http://192.186.0.1:8080';
}

通过 rewrite 指令,我们可以一目了然地看到路径是如何被重写的。

简单总结:proxy_pass 后面的地址带不带 /,取决于后端服务是否需要 location 匹配到的这个前缀(例如 /test)。如果需要保留,则不加 /;如果这个前缀只是我们用于在前端做路由拦截的,后端接口本身没有,那就需要加 / 将其去除。

延伸思考:location 后的斜杠又有什么区别?

那我们再进一步,看看下面两个配置:

# 配置一
location /test {
 proxy_pass 'http://192.186.0.1:8080';
}

# 配置二
location /test/ {
 proxy_pass 'http://192.186.0.1:8080';
}

这次的区别在于 location 指令后面是否有斜杠 /。那么,它们的区别是什么呢?

答案是:有区别!两者的匹配规则不同。

  • location /test前缀匹配,它会匹配以 /test 开头的所有路径,例如 /test/test//test/file/getList,甚至 /test123 也会被匹配到。
  • location /test/ 是更精准的匹配,它只匹配以 /test/ 开头的路径。例如 /test/file/getList 会被匹配,但 /test123 和单独的 /test 不会被匹配。

我们可以通过下表更直观地看清区别:

请求路径 /test /test/ 匹配结果
/test location /test
/test/ location /test/
/test/abc location /test/
/test123 location /test
/test-123 location /test

细心的你可能发现了一个问题:/test//test/abc 这两个请求同时被 location /testlocation /test/ 匹配到了,那么 Nginx 最终会使用哪个配置呢?

答案是:选择 location /test/

这就引出了 Nginx 中一个核心概念:location 匹配优先级。我们正好借此机会梳理一下它的规则。有个口诀可以帮助记忆:

等号精确第一名
波浪前缀挡正则
正则排队按顺序
普通前缀取最长

解释一下:

  • 等号 (=) 精确匹配:优先级最高。
  • 前缀优先匹配 (^~):如果匹配成功,则阻止后续的正则匹配。
  • *正则匹配 (~ 或 `~`)**:按照它们在配置文件中出现的顺序进行匹配,先到先得。
  • 普通前缀匹配(无符号):在所有都未匹配的情况下,选择匹配路径最长的那个。

说实话,这个口诀我也记不住。现在更高效的方式是,遇到复杂的配置需求,直接让 AI 助手帮你生成或检查 nginx.conf 文件,多调整几次就能得到想要的结果。技术的进步让我们能把精力更多放在逻辑设计上,而不是死记硬背语法细节。

下面我们通过一个模拟电商网站的完整 Nginx 配置案例,来直观感受一下不同优先级规则的实战应用:

server {
    listen 80;
    server_name shop.example.com;
    root /var/www/shop;

    # ==========================================
    # 1. 精确匹配 (=) - 最高优先级
    # ==========================================
    # 首页精确匹配 - 加快首页访问速度
    location = / {
        return 200 "欢迎来到首页 [精确匹配 =]";
        add_header Content-Type text/plain;
    }
    # robots.txt 精确匹配
    location = /robots.txt {
        return 200 "User-agent: *\nDisallow: /admin/";
        add_header Content-Type text/plain;
    }

    # ==========================================
    # 2. 前缀优先匹配 (^~) - 阻止正则匹配
    # ==========================================
    # 静态资源目录 - 不需要正则处理,直接命中提高性能
    location ^~ /static/ {
        alias /var/www/shop/static/;
        return 200 "静态资源目录 [前缀优先 ^~]";
    }
    # 阻止访问隐藏文件
    location ^~ /. {
        deny all;
        return 403 "禁止访问隐藏文件 [前缀优先 ^~]";
    }

    # ==========================================
    # 3. 正则匹配 (~ ~*) - 按顺序匹配
    # ==========================================
    # 图片文件处理 (区分大小写)
    location ~ \.(jpg|jpeg|png|gif|webp|svg|ico)$ {
        return 200 "图片文件 [正则匹配 ~]";
    }
    # CSS/JS 文件处理 (不区分大小写)
    location ~* \.(css|js)$ {
        return 200 "CSS/JS文件 [正则不区分大小写 ~*]";
    }
    # 禁止访问备份文件
    location ~ \.(bak|backup|old|tmp)$ {
        deny all;
        return 403 "禁止访问备份文件 [正则匹配 ~]";
    }

    # ==========================================
    # 4. 普通前缀匹配 - 最长匹配原则
    # ==========================================
    # API 接口 v2 (更长的前缀)
    location /api/v2/ {
        proxy_pass http://backend_v2;
        return 200 "API v2接口 [普通前缀,更长]";
    }
    # API 接口 v1 (较短的前缀)
    location /api/v1/ {
        proxy_pass http://backend_v1;
        return 200 "API v1接口 [普通前缀,较短]";
    }
    # API 接口通用 (最短的前缀)
    location /api/ {
        proxy_pass http://backend;
        return 200 "API通用接口 [普通前缀,最短]";
    }

    # ==========================================
    # 5. 通用匹配 - 兜底规则
    # ==========================================
    location / {
        try_files $uri $uri/ /index.html;
        return 200 "通用匹配 [兜底规则]";
    }
}

为了验证上述配置的匹配顺序,可以参考下表:

请求URI 匹配的Location 优先级类型 说明
/ = / 精确匹配 精确匹配优先级最高
/static/css/style.css ^~ /static/ 前缀优先 ^~ 阻止了后续的图片或CSS正则匹配
/images/logo.png ~ \.(jpg|jpeg|...)$ 正则匹配 匹配图片文件的正则规则
/api/v2/products /api/v2/ 普通前缀(最长) 遵循最长前缀匹配原则
/api/orders /api/ 普通前缀(最短) 匹配最短的通用前缀
/.git/config ^~ /. 前缀优先 被禁止访问隐藏文件的规则拦截

问题二:nginx -s reloadsystemctl reload nginx,哪个更优雅?

在服务器上,我们常用以下两种命令来重新加载 Nginx 配置:

# 命令一
nginx -s reload

# 命令二
systemctl reload nginx

这两个命令都能实现“优雅重启”(平滑加载新配置,不中断现有连接),但它们的执行方式和适用场景有区别。

nginx -s reload
这是 Nginx 原生的信号控制命令。它直接向 Nginx 的主进程发送 reload 信号。使用此命令需要确保 nginx 二进制文件在系统的 PATH 环境变量中,或者使用完整路径(如 /usr/sbin/nginx -s reload)。

systemctl reload nginx
这是通过 systemd 系统和服务管理器来操作 Nginx 服务。它是现代主流 Linux 发行版(如 CentOS 7/8、RHEL 7/8、Ubuntu 16.04+)管理服务的标准方式。

简单对比一下相关操作:

功能 Nginx 原生命令 systemctl 命令
平滑重载配置 nginx -s reload systemctl reload nginx
停止服务 nginx -s stopnginx -s quit systemctl stop nginx
启动服务 nginx (需带-c指定配置) systemctl start nginx
测试配置 nginx -t 无直接对应,仍需调用 nginx -t

对于使用 Systemd 的现代 Linux 服务器,推荐优先使用 systemctl,因为它是系统管理服务的标准,集成度更高(如日志管理、开机自启等)。一个优雅的操作流程通常是:

# 1. 首先测试配置文件语法是否正确
nginx -t

# 2. 如果测试通过,使用 systemctl 平滑重载配置
systemctl reload nginx

# 3. 检查服务状态
systemctl status nginx

# 如果 systemctl 不可用,再回退到原生命令
sudo nginx -s reload

养成先测试 (nginx -t) 后重载的习惯,能有效避免因配置语法错误导致服务异常。

问题三:离线环境如何安装 Nginx?

我们通常在能联网的服务器上通过包管理器安装 Nginx,例如在 Ubuntu/Debian 上:

sudo apt update
sudo apt install nginx

但如果遇到一台无法连接外网的服务器,该如何安装呢?这里提供两种方案。

方案一:下载预编译包进行离线安装

这是最推荐的方式。你需要先在能上网的机器上,根据目标服务器的架构和系统版本,从 Nginx 官方仓库下载对应的安装包。

1. 确定服务器架构和系统版本

# 查看系统架构
uname -m
# 输出 x86_64(Intel/AMD 64位)、aarch64(ARM 64位)等

# 查看系统版本信息
cat /etc/os-release

2. 下载对应的安装包
根据上一步的信息,到 Nginx 官方下载页面 或对应的包仓库找到合适版本的 RPM(CentOS/RHEL)或 DEB(Ubuntu/Debian)包。

3. 传输并安装
将下载好的包上传到目标服务器,然后执行安装:

# 对于 RPM 包 (CentOS/RHEL)
sudo rpm -ivh nginx-*.rpm

# 对于 DEB 包 (Ubuntu/Debian)
sudo dpkg -i nginx-*.deb

4. 启动并验证

sudo systemctl start nginx
sudo systemctl enable nginx
nginx -v
curl http://localhost

方案二:源码编译安装

这种方式更为复杂,通常只在有特殊定制需求(如添加第三方模块、指定安装路径)时使用。主要步骤包括:下载源码、安装编译依赖(如 gcc、PCRE、zlib、OpenSSL)、执行 ./configuremakemake install。对于大多数运维场景,方案一已足够。

问题四:如何正确配置 Nginx 以部署 Unity WebGL (WASM) 应用?

当你使用 Unity 发布 WebGL(最终输出为 WASM 文件)项目并用 Nginx 部署时,可能会遇到类似下面的错误:

Failed to load module script: The server responded with a non-JavaScript MIME type of "application/wasm".

这通常是因为 Nginx 没有正确配置 WASM 文件的 MIME 类型。部署 WASM 应用需要一些特殊配置。

第一部分:配置正确的 MIME 类型

找到 Nginx 配置文件目录下的 mime.types 文件(通常在 /etc/nginx/mime.types/usr/local/nginx/conf/mime.types),确保其中包含以下行:

application/wasm        wasm;

如果不存在,请手动添加。

第二部分:详细的服务器配置

以下是一个针对 Unity WebGL 应用的完整 Nginx 配置示例,重点关注 MIME 类型、缓存和压缩设置:

server {
    listen 80;
    server_name your-domain.com; # 请修改为你的域名或IP
    root /var/www/unity-webgl; # 修改为你的构建文件存放目录
    index index.html;

    # WASM 文件(必须正确设置 MIME 类型)
    location ~ \.wasm$ {
        # 明确指定 MIME 类型
        types {
            application/wasm wasm;
        }
        # 长期缓存,WASM 文件通常不会变
        add_header Cache-Control "public, max-age=31536000, immutable";
        # 如果需要跨域则添加,同域可省略
        add_header Access-Control-Allow-Origin *;
    }

    # 处理压缩的 WASM 文件(如 .wasm.gz)
    location ~ \.wasm\.gz$ {
        add_header Content-Encoding gzip; # 告诉浏览器这是gzip压缩的
        add_header Content-Type application/wasm; # 设置正确的类型
        add_header Cache-Control "public, max-age=31536000, immutable";
    }

    # Data 文件(Unity 资源数据)
    location ~ \.data$ {
        types {
            application/octet-stream data;
        }
        add_header Cache-Control "public, max-age=31536000, immutable";
    }
    location ~ \.data\.gz$ {
        add_header Content-Encoding gzip;
        add_header Content-Type application/octet-stream;
        add_header Cache-Control "public, max-age=31536000, immutable";
    }

    # JavaScript 文件
    location ~ \.js$ {
        types {
            application/javascript js;
        }
        add_header Cache-Control "public, max-age=31536000, immutable";
    }
    location ~ \.js\.gz$ {
        add_header Content-Encoding gzip;
        add_header Content-Type application/javascript;
        add_header Cache-Control "public, max-age=31536000, immutable";
    }

    # Unity 特定的目录和资源
    location /StreamingAssets/ {
        add_header Cache-Control "public, max-age=31536000, immutable";
    }
    location /Build/ {
        add_header Cache-Control "public, max-age=31536000, immutable";
    }
    location /TemplateData/ {
        add_header Cache-Control "public, max-age=86400";
    }

    # HTML 文件不应长时间缓存,以确保更新及时生效
    location ~ \.html$ {
        add_header Cache-Control "no-cache, no-store, must-revalidate";
        add_header Pragma "no-cache";
        add_header Expires "0";
    }

    # 通用处理规则
    location / {
        try_files $uri $uri/ /index.html;
    }

    # 开启 Gzip 压缩以提升性能
    gzip on;
    gzip_vary on;
    gzip_types
        text/plain
        text/css
        application/javascript
        application/json
        application/wasm # 记得加入 wasm
        application/octet-stream;
}

核心要点总结

  1. MIME 类型是根本:必须在 mime.types 文件中添加 application/wasm wasm;,这是浏览器正确识别和执行 WASM 文件的前提。
  2. 区分压缩文件:如果提供了 .gz 等压缩文件,需通过 add_header Content-Encoding 正确响应,否则浏览器无法解压。
  3. 合理的缓存策略:WASM、Data、JS 等构建产物几乎不变,应设置长期缓存(immutable)。而 index.html 是入口,应禁用缓存以确保用户总能获取到最新版本。
  4. 性能与安全:开启 Gzip 压缩提升传输效率。可以酌情添加一些安全响应头,如 X-Content-Type-Options: nosniff

希望以上关于 Nginx 配置细节、运维命令和特殊应用部署的分享,能帮助你更深入地理解这个强大的 Web 服务器。如果你在配置过程中遇到其他问题,欢迎到云栈社区与其他开发者一起交流探讨。




上一篇:分库分表后非分表键查询难题:四种主流解决方案与实战避坑指南
下一篇:Claude Code 与 OpenClaw 深度对比分析:从“听命令”到“懂你”的 AI Agent 生态位之争
您需要登录后才可以回帖 登录 | 立即注册

手机版|小黑屋|网站地图|云栈社区 ( 苏ICP备2022046150号-2 )

GMT+8, 2026-3-12 08:56 , Processed in 0.543387 second(s), 41 queries , Gzip On.

Powered by Discuz! X3.5

© 2025-2026 云栈社区.

快速回复 返回顶部 返回列表