CodeRoadMap
首页路线题库资源库文章社区
CodeRoadMap

程序员的学习成长路线图。内容免费开放,登录后可同步路线图与课时进度。

由 程序员柏风 独立维护 · 关于作者

学习

  • 路线图
  • 课程
  • 面试题
  • 文章
  • 我

更多

  • 资源
  • 社区
  • 关于作者
  • 内容说明

© 2026 CodeRoadMap

津ICP备2026012044号-1·津公网安备12011002026155号·coderoadmap@126.com
DevOps 入门实战/课时/Nginx
RawBlame
课时目录12 节

DevOps 入门实战

  1. 01DevOps 入门
  2. 02操作系统
  3. 03Git 与 GitHub
  4. 04脚本语言
  5. 05网络协议
  6. 06Docker
  7. 07AWS
  8. 08Terraform
  9. 09Ansible
  10. 10GitHub Actions
  11. 11Nginx
  12. 12综合练习

Nginx

反向代理入门。

25 分钟

Nginx

这一课你将能做到

  1. 配置 Nginx 反向代理:把 443 端口的 HTTPS 流量转发给本机 8080 的 Spring Boot
  2. 配置静态资源服务、gzip 压缩、访问日志与限流等生产必备项
  3. 用 nginx -t 验证配置并优雅重载,定位 502/504 等常见网关错误

为什么要学

你的 Spring Boot 应用监听 8080 端口,但用户访问的是 https://api.example.com 的 443 端口。中间的转换——HTTPS 卸载、域名匹配、请求转发、静态文件直出、负载均衡——就是 Nginx 干的活。它是几乎所有 Java 后端的「门面」:一台低配 Nginx 能扛几万并发连接,而它身后的 Java 应用只需要专注业务。

人话:Nginx 是餐厅的前台。顾客(用户)不直接进厨房(Java 应用),而是先找前台:前台收下点单(接收请求)、转给厨房(反向代理)、把菜端出去(返回响应)。加菜加人(扩容)时,前台还能把顾客均匀分到多个厨房窗口(负载均衡)。

先建立图像

用户 HTTPS 请求
     │ 443
     ▼
┌─────────────────────────────────────────┐
│                 Nginx                    │
│  ┌──────────────┐    ┌────────────────┐ │
│  │ TLS 卸载      │ →  │ location 路由   │ │
│  │ (证书/密钥)   │    │ /api/* → 后端   │ │
│  └──────────────┘    │ /*     → 静态文件│ │
│                      └───────┬────────┘ │
└──────────────────────────────┼──────────┘
                               │ proxy_pass http://127.0.0.1:8080
                               ▼
                      Spring Boot (8080)
                               │
                    ┌──────────┴──────────┐
                    ▼                     ▼
                MySQL (3306)         Redis (6379)

Nginx 配置的核心结构:

nginx.conf
├── events { worker_connections }     ← 全局:连接模型
└── http {                            ← HTTP 层:gzip、日志格式、限流区
    ├── upstream { 后端列表 }          ← 负载均衡池
    └── server {                      ← 虚拟主机:按域名区分站点
        ├── listen 443 ssl
        ├── server_name
        └── location { proxy_pass }   ← URL 路由规则
    }

跟着做

步骤 1:安装与基础命令

# Ubuntu
sudo apt install nginx -y

# 配置文件位置
ls /etc/nginx/           # nginx.conf 主配置,conf.d/ 和 sites-enabled/ 放站点配置

# 四个保命命令
sudo nginx -t                      # 改完配置先验证语法!
sudo systemctl reload nginx        # 优雅重载:不断开现有连接
sudo tail -f /var/log/nginx/access.log
sudo tail -f /var/log/nginx/error.log

步骤 2:反向代理 Spring Boot(核心配置)

创建 /etc/nginx/conf.d/myapp.conf:

server {
    listen 80;
    server_name api.example.com;

    # HTTP 全部 301 到 HTTPS(证书配好后启用)
    # return 301 https://$host$request_uri;

    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;
        proxy_set_header X-Forwarded-Proto $scheme;

        # 超时控制:防止后端慢请求拖死连接
        proxy_connect_timeout 5s;
        proxy_read_timeout 60s;
    }
}

验证并生效:

sudo nginx -t && sudo systemctl reload nginx
curl -H "Host: api.example.com" http://127.0.0.1/actuator/health

步骤 3:接入 HTTPS(结合第 5 课的 certbot)

sudo certbot --nginx -d api.example.com
# certbot 会自动改写配置,加入 ssl_certificate 等指令

改写后的关键部分:

server {
    listen 443 ssl;
    server_name api.example.com;
    ssl_certificate     /etc/letsencrypt/live/api.example.com/fullchain.pem;
    ssl_certificate_key /etc/letsencrypt/live/api.example.com/privkey.pem;

    # TLS 安全基线
    ssl_protocols TLSv1.2 TLSv1.3;
    ssl_session_cache shared:SSL:10m;

    location / { ... }   # 同步骤 2
}

步骤 4:静态资源直出 + gzip(给后端减负)

本站前端打包后是一堆 JS/CSS/图片,完全没必要让 Java 处理:

server {
    listen 443 ssl;
    server_name www.example.com;

    root /var/www/frontend/dist;
    index index.html;

    # gzip 压缩,文本资源体积减 70%
    gzip on;
    gzip_types text/css application/javascript application/json image/svg+xml;
    gzip_min_length 1k;

    # 静态资源强缓存(带 hash 的文件名放心缓存一年)
    location /assets/ {
        expires 365d;
        add_header Cache-Control "public, immutable";
    }

    # SPA 路由:找不到的文件都回退到 index.html
    location / {
        try_files $uri $uri/ /index.html;
    }

    # API 请求转发给后端
    location /api/ {
        proxy_pass http://127.0.0.1:8080;
        proxy_set_header Host $host;
        proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
    }
}

步骤 5:负载均衡(两台 Spring Boot 分摊流量)

upstream myapp_backend {
    least_conn;                            # 最少连接优先(默认是轮询)
    server 127.0.0.1:8080 max_fails=3 fail_timeout=30s;
    server 127.0.0.1:8081 max_fails=3 fail_timeout=30s;
}

server {
    listen 443 ssl;
    server_name api.example.com;

    location / {
        proxy_pass http://myapp_backend;   # 指向 upstream 而非单个地址
        ...
    }
}

步骤 6:限流——防刷接口的基础手段

http {
    # 定义限流区:按客户端 IP,每秒 10 个请求
    limit_req_zone $binary_remote_addr zone=api_limit:10m rate=10r/s;

    server {
        location /api/auth/ {              # 只给登录注册等敏感接口限流
            limit_req zone=api_limit burst=20 nodelay;
            proxy_pass http://127.0.0.1:8080;
        }
    }
}

讲清楚要点

  1. 反向代理是 Nginx 的第一职责:对外统一入口(443)、对内分发到后端(8080)。好处:后端不暴露公网、HTTPS 集中在 Nginx 处理、后端重启时 Nginx 返回友好错误页。
  2. proxy_set_header 四件套不能省:不配的话,Java 里 request.getRemoteAddr() 拿到的永远是 127.0.0.1,审计日志、限流、风控全部失效。X-Forwarded-For 是真实客户端 IP。
  3. nginx -t 再 reload 是铁律:配置有语法错误时 reload 会失败(但旧进程还在跑,不影响线上),直接 restart 有错误配置就会挂掉。养成习惯:改配置 → -t → reload。
  4. 静态资源让 Nginx 直出:Nginx 用 sendfile 零拷贝发送文件,性能比 Java 高一个数量级,还能直接配缓存头。Java 只处理动态 API。
  5. 错误日志优先看 error.log:502(后端挂了/端口不通)、504(后端处理超时)、499(客户端主动断开),错误日志里都有明确记录,别对着 access.log 瞎猜。

你可能会懵的地方

  • 502 和 504 怎么区分? 502 Bad Gateway:Nginx 根本连不上后端(Java 进程挂了、端口错了、防火墙拦了)。504 Gateway Timeout:连上了,但后端在 proxy_read_timeout 内没返回(慢 SQL、死循环)。先 502 查进程端口,再 504 查后端性能。

  • location 匹配规则太多记不住? 优先级:location = /exact(精确)> location ^~ /prefix(前缀优先)> location ~ \.php$(正则)> location /prefix(普通前缀)> location /(兜底)。记住最常用的:/api/ 前缀转发 + / 兜底给静态。

  • proxy_pass 结尾加不加 / 有什么区别? proxy_pass http://127.0.0.1:8080;(无尾斜杠):location /api/ 收到 /api/users → 后端收到 /api/users。proxy_pass http://127.0.0.1:8080/;(有尾斜杠):/api/users → 后端收到 /users,前缀被剥掉。后端有没有 /api 前缀决定你选哪种。

  • Nginx 一台能扛多少并发? 官方数据单机上万并发很轻松,瓶颈通常在带宽和后端,而不是 Nginx。它的事件驱动模型(一个 worker 处理几千连接)和 Java 的「一个连接一个线程」完全不同。

  • 为什么部署后前端页面显示旧版本? 两个缓存层:Nginx 的 expires 强缓存 + 浏览器缓存。发版时 index.html 必须 no-cache(每次都问服务器),带 hash 的 assets 才用长缓存。

错法对照

错法问题正解
不传 X-Forwarded-For后端拿不到真实 IPproxy_set_header 四件套
改完配置直接 restart配置错误时全站挂掉先 nginx -t,再 reload
8080 端口对公网开放绕过 Nginx 直连后端,HTTPS 失效安全组只放 80/443,8080 只绑 127.0.0.1
proxy_read_timeout 不设慢请求把 worker 占满显式设置超时时间
index.html 也缓存一年发版后用户看到旧页面html 设 no-cache,hash 文件设长缓存

卡点急救

  1. reload 后还是旧配置:确认 nginx -t 通过且 reload 无报错;确认改的是实际加载的文件(nginx -T 看完整生效配置);多 server_name 冲突时第一个匹配的生效。
  2. 大文件上传 413 或 502:默认 body 限制 1MB,加 client_max_body_size 50m;;上传慢还要调大 proxy_read_timeout。
  3. WebSocket 握手失败 400:需要额外两个头——proxy_set_header Upgrade $http_upgrade; proxy_set_header Connection "upgrade";,并把 proxy_read_timeout 调大到 3600s 防断连。

面试常问

  • Nginx 为什么能扛高并发? 事件驱动 + epoll 异步非阻塞模型:少量 worker 进程(通常 = CPU 核数),每个 worker 通过事件循环处理数千连接,遇到 IO 等待立刻切换处理其他请求,不阻塞。对比传统「一连接一线程」模型,内存和上下文切换成本低几个数量级。

  • 正向代理和反向代理的区别? 正向代理代理的是客户端(科学上网工具:服务器不知道真实用户是谁);反向代理代理的是服务端(Nginx:用户不知道真实后端是哪台)。一句话:正向隐藏客户端,反向隐藏服务端。

  • Nginx 负载均衡有哪些策略? 轮询(默认)、weight 权重(性能好的机器多分担)、least_conn 最少连接(请求耗时不均时最优)、ip_hash(同一 IP 固定到一台,用于无共享 session 的老系统——新系统应用 Redis 共享 session 代替)。

自测三问

  1. 用户请求 https://api.example.com/api/users,写出请求经过 Nginx 到 Spring Boot 的完整路径,包括用到的配置指令。
  2. 线上突然出现大量 504,你的排查顺序是什么?
  3. 为什么发版后要用 hash 文件名 + 长缓存,而 index.html 必须 no-cache?

小结一句话

Nginx 是应用的统一门面:TLS、路由、静态资源、负载均衡、限流,一个配置全部搞定。

延伸

  • Nginx 官方文档
  • 下一课:综合练习——把前 11 课串成一条完整的真实部署链路。
加载本节练习…
← 上一课GitHub Actions下一课 →综合练习