上周那篇 nginx.conf 配置指南,我们把 location 匹配规则讲清楚了。
配完之后,我信心满满地把服务从1台扩到3台,心想这下高可用了。
结果活动当天,一台服务器挂了,用户开始报错。
查了半天才发现,原来我踩了一堆坑。
今天这篇文章,就聊聊我当时遇到的几个坑,以及后来怎么填的。
坑点一:配了 upstream,Nginx 还是只往一台发
单机时代,我的配置长这样:
location /api/ {
proxy_pass http://192.168.1.100:8080;
}
这配置在单机时代没问题。但扩到3台后,我傻眼了:Nginx 只认第一台。
后来才知道,需要用 upstream 把后端服务器定义成一个“组”:
upstream backend {
server 192.168.1.100:8080;
server 192.168.1.101:8080;
server 192.168.1.102:8080;
}
location /api/ {
proxy_pass http://backend;
}
加上这几行,Nginx 开始自动把请求分给三台服务器。
坑填了,但紧接着又踩了下一个。
坑点二:轮询导致 session 老丢
默认的负载均衡算法是轮询(Round Robin),每个请求依次分给下一台服务器。
我刚开始觉得轮询挺好,公平。结果用户反馈说登录状态老丢。
查了半天才知道,轮询会把同一个用户的请求分给不同的服务器。如果 session 没共享,就会出问题。
后来我换成了 ip_hash:
upstream backend {
ip_hash;
server 192.168.1.100:8080;
server 192.168.1.101:8080;
server 192.168.1.102:8080;
}
ip_hash 会根据客户端 IP 的 hash 结果分配服务器。同一个 IP 永远访问同一台服务器,session 问题就解决了。
几种算法怎么选,我后来整理了一下:
| | |
|---|
| | |
| weight=3 | |
| ip_hash; | |
| least_conn; | |
坑点三:服务器挂了,Nginx 还在转发
回到开头那个问题:服务器挂了,用户报错。
这就是健康检查没配。
Nginx 的健康检查是被动的——它不会主动去 ping 后端,而是在转发请求时发现失败,才把服务器标记为“不健康”。
配置方式是在 server 后面加上 max_fails 和 fail_timeout:
upstream backend {
server 192.168.1.100:8080 max_fails=3 fail_timeout=30s;
server 192.168.1.101:8080 max_fails=3 fail_timeout=30s;
server 192.168.1.102:8080 max_fails=3 fail_timeout=30s;
}
参数含义:
fail_timeout=30s:失败达到3次后,暂停转发30秒
30秒后,Nginx 会再尝试一次。如果后端恢复了,就重新标记为健康。
加上这个之后,那次活动后面再没出过问题。
坑点四:正在处理的请求直接报错
健康检查只能让 Nginx 不再转发新请求给挂了的服务器。但正在处理的那个请求怎么办?直接报错?
Nginx 提供了一个指令叫 proxy_next_upstream:
location /api/ {
proxy_pass http://backend;
# 遇到这些情况,自动转发给下一台服务器
proxy_next_upstream error timeout http_500 http_502 http_503;
# 最多尝试2次
proxy_next_upstream_tries 2;
}
现在,当 Nginx 向第一台服务器发送请求时,如果遇到网络错误、超时,或者后端返回 500/502/503,它会自动把这个请求转发给下一台健康服务器。
用户几乎感知不到后端发生了故障。
坑点五:WebSocket 连不上
我有个项目用了 WebSocket(在线聊天)。配完反向代理后,WebSocket 死活连不上。
后来才知道,WebSocket 是 HTTP 协议“升级”来的。Nginx 默认不认识这个协议,需要手动配几行:
location /ws/ {
proxy_pass http://backend;
# WebSocket 需要 HTTP/1.1
proxy_http_version 1.1;
# 传递 Upgrade 和 Connection 头
proxy_set_header Upgrade $http_upgrade;
proxy_set_header Connection "upgrade";
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
}
加上这几行,WebSocket 就通了。
我的最终配置
经过这几个坑,我的反向代理配置现在长这样:
# 定义后端服务器组
upstream backend {
# 负载均衡算法(根据场景选)
ip_hash;
# 健康检查配置
server 192.168.1.100:8080 max_fails=3 fail_timeout=30s;
server 192.168.1.101:8080 max_fails=3 fail_timeout=30s;
server 192.168.1.102:8080 backup; # 备用服务器,主服务器全挂时才启用
}
server {
listen 80;
server_name yourdomain.com;
# 普通 HTTP 请求
location /api/ {
proxy_pass http://backend;
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
# 故障转移
proxy_next_upstream error timeout http_500 http_502 http_503;
proxy_next_upstream_tries 2;
}
# WebSocket 请求
location /ws/ {
proxy_pass http://backend;
proxy_http_version 1.1;
proxy_set_header Upgrade $http_upgrade;
proxy_set_header Connection "upgrade";
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
}
}
阅读原文:原文链接
该文章在 2026/4/14 15:19:53 编辑过