【计算基础|网络16】【工程实战】Nginx 部署与反向代理

【计算基础|网络16】【工程实战】Nginx 部署与反向代理

前面从网络层一路讲到 Socket 手写,真实生产里你几乎不会自己写 HTTP 服务的网络层------都是把后端服务(FastAPI/Node/Go)跑在内网端口上,前面架一层 Nginx 统一对外。这篇就是把这件事从安装到压测真机跑一遍:反向代理、负载均衡、location 匹配、静态资源、HTTPS、跨域、日志,全部有实测输出。

环境:macOS(Apple Silicon),Nginx 1.31.6(brew install nginx),FastAPI + uvicorn 当后端,ab 压测。配置文件路径 /opt/homebrew/etc/nginx/;Ubuntu 对应 /etc/nginx/,命令基本一致。


一、Nginx 是什么:事件驱动的高性能服务器

一句话:Nginx 是高性能 HTTP 服务器、反向代理和负载均衡器 ,核心是事件驱动、异步非阻塞,单机抗并发能力极强。

它和老牌 Apache 的区别,一张表说清:

维度 Nginx Apache
并发模型 事件驱动、异步非阻塞 进程/线程每连接一个
高并发表现 几万连接轻松 hold 连接多了吃内存、性能掉
资源占用 低 相对高
静态文件 直接内核 sendfile,极快 一般
配置/扩展 配置简洁,模块动态 .htaccess 灵活但慢

🔴 结论:现在生产上"前端静态资源 + 反向代理 + 负载均衡"这一层,Nginx 是事实标准。


二、安装与启停

2.1 三种安装方式

bash 复制代码
# macOS(Apple Silicon 配置在 /opt/homebrew/etc/nginx/;Intel 在 /usr/local/etc/nginx/)
brew install nginx

# Ubuntu
sudo apt install nginx

# Docker
docker run -d -p 80:80 --name nginx nginx

我本机 brew install nginx 后,nginx -v 输出 nginx/1.31.6。Homebrew 版本为了免 sudo ,把默认端口设成了 8080 ,并且会自动加载 /opt/homebrew/etc/nginx/servers/ 下的所有配置。

2.2 启停与重载

命令 作用
nginx 启动
nginx -s stop 快速停止
nginx -s reload 重载配置(不中断服务,最常用)
nginx -t 测试配置语法(改完配置先跑这个!)
nginx -v 看版本

⚠️ 改完配置第一件事永远是 nginx -t,语法错了 reload 会失败或服务起不来。生产 reload 是无感的。


三、配置文件结构:一层套一层

Nginx 配置从上到下是嵌套的:全局块 → events 块 → http 块 → server 块 → location 块。

要点:

  • http 包含多个 server (一个 server 就是一个站点/虚拟主机),server 包含多个 location(按 URL 路径分流)。
  • 每条指令以分号结尾,块用 {}。
  • Homebrew 主配置 nginx.conf 末尾有 include servers/*;,意思是把 servers/ 目录下所有文件都当配置读进来------我们新增站点就往这个目录丢文件,不用动主配置。

常用块指令速查:

块 常用指令
全局 worker_processes auto(CPU 核数)、error_log、pid
events worker_connections 1024、use epoll(Linux)/kqueue(macOS)
http include mime.types、sendfile on、keepalive_timeout、gzip on

四、反向代理:把请求转发给后端

4.1 正向代理 vs 反向代理

正向代理 反向代理
代理对象 客户端 服务端
典型 VPN、翻墙代理,帮客户端访问外面 Nginx,帮服务端接收请求
客户端感知 知道目标真实地址 不知道后端有几台、是谁
用途 突破限制、隐藏客户端 负载均衡、缓存、SSL 终结、隐藏后端

一句话:正向代理站在客户端那边,反向代理站在服务端这边。

4.2 基本配置

核心就一句:proxy_pass http://后端地址;。客户端访问 Nginx,Nginx 再把请求转发给 FastAPI。

nginx 复制代码
location /api/ {
    proxy_pass http://127.0.0.1:8000;
    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    30s;
}

proxy_set_header 这几行很关键------后端拿到的"客户端真实 IP"其实是 Nginx 的 IP ,所以 Nginx 要把真实客户端 IP 写进 X-Real-IP / X-Forwarded-For 头带给后端,后端从这两个头里取真实 IP。

4.3 真机跑通

我起了两个 FastAPI 后端(8001、8002),它们的 /api/headers 会把收到的请求头原样打出来。请求打到 Nginx(8081)再转发:

bash 复制代码
curl -s http://127.0.0.1:8081/api/headers

实测输出------后端确实收到了 Nginx 透传的真实客户端 IP:

json 复制代码
{
  "backend_port": "8001",
  "host": "127.0.0.1",
  "x_real_ip": "127.0.0.1",
  "x_forwarded_for": "127.0.0.1",
  "user_agent": "curl/8.7.1"
}

✅ 反向代理成立:客户端只访问 127.0.0.1:8081,根本不知道后端其实是 8001 这个 FastAPI 进程。


五、location 匹配规则:请求落到哪块

5.1 修饰符与优先级

nginx 复制代码
location [=|~|~*|^~] /uri/ { ... }

优先级从高到低:

顺序 修饰符 含义
1 = 精确匹配 ,如 location = /
2 ^~ 前缀匹配,命中后不再查正则
3 ~ / ~* 正则 匹配(~ 区分大小写,~* 不区分)
4 普通前缀 不带修饰符的前缀匹配
5 / 通用兜底

口诀:精确 > 前缀^~ > 正则 > 普通前缀 > 兜底。


六、负载均衡:多台后端怎么分流量

6.1 upstream 与四种策略

用 upstream 定义一组后端,proxy_pass 指向它就行。策略:

策略 写法 说明
round-robin(默认) 不写 轮询,一个一个轮流
weight server x weight=3; 加权轮询,性能好的机器多分
ip_hash ip_hash; 按客户端 IP 哈希,会话保持
least_conn least_conn; 分给当前连接数最少的
hash hash $request_uri; 自定义键哈希

服务器还能带状态参数:backup(备用,全挂了才上)、down(停用)、max_fails、fail_timeout。

6.2 实测:weight 3:1 分发

我的配置:

nginx 复制代码
upstream blog_backend {
    server 127.0.0.1:8001 weight=3;
    server 127.0.0.1:8002 weight=1;
}

server {
    listen 8081;
    location /api/ {
        proxy_pass http://blog_backend;
    }
}

连续发 8 次请求,看后端端口怎么分:

bash 复制代码
for i in $(seq 1 8); do curl -s http://127.0.0.1:8081/api/hello; echo; done

实测输出------8001 接到 6 次、8002 接到 2 次,正好 3:1:

text 复制代码
{"msg":"hello","backend_port":"8001"}
{"msg":"hello","backend_port":"8001"}
{"msg":"hello","backend_port":"8002"}
{"msg":"hello","backend_port":"8001"}
{"msg":"hello","backend_port":"8001"}
{"msg":"hello","backend_port":"8001"}
{"msg":"hello","backend_port":"8002"}
{"msg":"hello","backend_port":"8001"}

✅ 负载均衡生效。流量被 Nginx 按权重分散到了两个后端实例上------这就是"扩容多台后端,前面一个入口"的原理。


七、静态资源、缓存与上传限制

7.1 root vs alias

这俩最容易混:

指令 行为 例子(请求 /static/index.html)
root 拼接路径 root /var/www → /var/www/static/index.html
alias 替换路径 alias /data/ → /data/index.html

我用 alias 把 /static/ 映射到本地目录:

nginx 复制代码
location /static/ {
    alias /path/to/static/;
    index index.html;
    expires 1h;     # 浏览器缓存 1 小时
}

实测访问:

bash 复制代码
curl -s http://127.0.0.1:8081/static/
# <h1>Hello from Nginx static</h1>

7.2 上传大小限制

nginx 复制代码
client_max_body_size 10m;

默认才 1m,上传大文件报 413 Request Entity Too Large 就是它太小。这是新手最常踩的坑之一。


八、用反向代理解决跨域

8.1 原理

浏览器跨域是同源策略 搞的鬼。解法很简单:让前端和 API 在同一个域名下 ------前端静态资源由 Nginx 服务,/api/ 路径也由 Nginx 转发到后端,浏览器看全是同源,根本不触发跨域。

nginx 复制代码
server {
    listen 80;
    location / {
        root /path/to/frontend;     # 前端静态文件
    }
    location /api/ {
        proxy_pass http://127.0.0.1:8000;   # 后端
    }
}

另一种是直接让 Nginx 加 CORS 头:

nginx 复制代码
add_header Access-Control-Allow-Origin *;

九、配置 HTTPS

9.1 证书从哪来

  • Let's Encrypt :免费、自动续期,生产用 certbot 申请。
  • 自签名证书:测试用,浏览器会报不安全。

9.2 生成自签名证书

bash 复制代码
openssl req -x509 -newkey rsa:2048 -keyout key.pem -out cert.pem -days 365 -nodes \
  -subj "/CN=localhost"

9.3 Nginx HTTPS 配置

nginx 复制代码
server {
    listen 8443 ssl;
    ssl_certificate     /path/to/cert.pem;
    ssl_certificate_key /path/to/key.pem;
    ssl_protocols       TLSv1.2 TLSv1.3;

    location /api/ {
        proxy_pass http://127.0.0.1:8000;
    }
}

实测(自签名证书,-k 跳过校验):

bash 复制代码
curl -sk https://127.0.0.1:8443/api/hello
# {"msg":"hello","backend_port":"8001"}

✅ HTTPS 通了。生产再配 HTTP 强制跳 HTTPS:return 301 https://$host$request_uri;。


十、日志:出问题先看它

日志 记录什么
access_log 每个请求:谁、什么时候、访问什么、状态码、耗时
error_log 错误,级别 debug/info/notice/warn/error/crit

我本机默认访问日志 /opt/homebrew/var/log/nginx/access.log,实测内容:

text 复制代码
127.0.0.1 - - [22/Sep/2026:11:56:41 +0800] "GET /api/hello HTTP/1.1" 200 37 "-" "curl/8.7.1"

常用日志变量:$remote_addr(客户端 IP)、$request(请求行)、$status(状态码)、$body_bytes_sent、$http_user_agent、$request_time、$upstream_response_time(后端耗时)。

实时盯日志:tail -f /opt/homebrew/var/log/nginx/access.log。


十一、压测:直连 vs 走 Nginx

最后用 ab(ApacheBench)真机对比。后端是单进程 uvicorn:

bash 复制代码
# 直连后端:1000 请求、10 并发
ab -n 1000 -c 10 http://127.0.0.1:8001/api/hello

# 走 Nginx 代理:同样参数
ab -n 1000 -c 10 http://127.0.0.1:8081/api/hello

实测结果:

指标 直连后端(8001) 走 Nginx 代理(8081)
Failed requests 0 0
Requests per second 10707.90 #/sec 17869.59 #/sec
Time per request 0.934 ms 0.560 ms

🔴 有意思的结论:走 Nginx 代理的 QPS 反而更高。原因是 Nginx 事件驱动、异步非阻塞,而单进程 uvicorn 在高并发下处理连接效率不如 Nginx 这个"专业守门人"。这也印证了 Nginx 的价值------它不只是转发,本身就是个高性能并发层。


十二、Nginx 常见问题速查表

现象 原因 解决
413 Request Entity Too Large client_max_body_size 太小 调大,如 10m
404 location 匹配不到 / root 路径不对 检查 location 和 root/alias
502 Bad Gateway 后端没启动 / proxy_pass 地址错 确认后端在跑、地址端口对
504 Gateway Timeout 后端响应超时 调大 proxy_read_timeout
静态资源 403 权限问题 / 首页文件不存在 检查目录权限、index 文件
改配置不生效 忘 reload / 语法错 先 nginx -t 再 nginx -s reload

总结:你真正需要记住的 9 件事

  1. Nginx = 高性能 HTTP 服务器 + 反向代理 + 负载均衡,事件驱动、异步非阻塞。
  2. 配置结构:全局 → events → http → server → location ;新增站点丢进 servers/ 即可。
  3. 改完配置先 nginx -t,再 nginx -s reload(无感重载)。
  4. 反向代理核心就一句 proxy_pass,并用 X-Real-IP/X-Forwarded-For 把真实客户端 IP 带给后端。
  5. 正向代理代理客户端,反向代理代理服务端。
  6. 负载均衡用 upstream,策略有轮询/weight/ip_hash/least_conn;weight 按比例分流量。
  7. location 优先级:精确 = > ^~ > 正则 ~/~* > 普通前缀 > / ;root 拼接、alias 替换。
  8. client_max_body_size 默认 1m,传大文件报 413 就调它。
  9. HTTPS 用 listen 443 ssl + 证书;跨域靠"前后端同源 + Nginx 代理"最省事。

验证清单

  • 能用 brew install nginx 或 apt install nginx 装好,并找到配置文件路径
  • 会用 nginx -t 测配置、nginx -s reload 重载
  • 能写出一个反向代理配置,把 /api/ 转发到本地后端
  • 能解释为什么后端要从 X-Real-IP 取真实客户端 IP
  • 能配 upstream + weight 做负载均衡,并实测流量按比例分发
  • 能说清 location 五种修饰符的优先级
  • 能区分 root 和 alias
  • 遇到 413/502/504 能分别说出原因和改法
  • 能用 openssl 生成自签名证书并配出 HTTPS

参考资源


整个网络模块回顾

从网络 01 到 16,我们把计算机网络这条线走完了:

  • 模型与全景(网络 01):五层模型、一次请求的完整旅程。
  • HTTP 实战(网络 03、04):请求报文组成、方法语义、参数位置、JSON/表单/文件、鉴权与跨域排错。
  • 网络层(网络 14):IP 寻址与路由、子网划分、ICMP、ping/traceroute 的底层原理。
  • Socket 上手(网络 15):亲手写 TCP/UDP 程序、复现粘包、长度头解决、selectors 并发雏形。
  • 工程实战(网络 16,本篇):用 Nginx 把这些协议落地成生产架构------反向代理藏后端、负载均衡抗扩容、HTTPS 保安全、日志排错。

一句话串起来:应用层你写 HTTP 接口 → 传输层 TCP 负责可靠送达 → 网络层 IP 负责寻址路由 → 最前面 Nginx 把这一切统一收口。网络模块到此收官,下一个模块我们转入操作系统,去看 epoll、进程、线程、内存这些"应用程序跑起来"的另一半故事。

相关推荐
盛世宏博智慧档案1 小时前
恒湿净化一体机联动开发:档案馆温湿度阈值判断与启停控制实践
java·后端·struts·恒湿机·加湿除湿消毒净化一体机
兔子零10241 小时前
FastAPI 零基础入门教程:写过 Node.js,就能轻松学会 Python 后端开发(一)
前端·后端·python
huaweichenai1 小时前
spring boot实现base64上传
java·spring boot·后端
ThinkerQAQ_1 小时前
并发编程(九):BlockingQueue 与 Channel——语言层的原子性、可见性与有序性
后端
点纭2 小时前
spring ai1.0实战:ChatClient(2)
java·后端·spring
Gopher_HBo2 小时前
Resolver 与 Balancer —— 连接管理三层联动
后端
yunwei372 小时前
eBPF 实例教程:BPF Arena 零拷贝共享内存
linux·后端·性能优化
UrbanJazzerati2 小时前
AutoHotkey v2 零基础快速入门 Part 1
后端
geovindu2 小时前
rust: pool Pattern
开发语言·后端·设计模式·rust·对象池简写模式