第4章:反向代理与负载均衡
4.1 反向代理基础
4.1.1 什么是反向代理?
反向代理(Reverse Proxy)是指代理服务器接收客户端请求,然后将请求转发给内部网络中的服务器,并将从服务器得到的结果返回给客户端。
正向代理 vs 反向代理:
- 正向代理:客户端知道目标服务器,代理服务器代表客户端向目标服务器发送请求
- 反向代理:客户端不知道实际处理请求的服务器,代理服务器代表服务器接收客户端请求
反向代理的优势:
- 隐藏真实服务器IP地址,提高安全性
- 负载均衡,将请求分发到多个服务器
- 缓存静态资源,减轻后端服务器压力
- 提供SSL/TLS加密,卸载后端服务器的加密负担
- 实现访问控制和身份验证
4.1.2 proxy_pass指令详解
proxy_pass是Nginx实现反向代理的核心指令,用于将请求转发到后端服务器。
基本语法:
location /path/ {
proxy_pass http://backend_server;
}配置示例:
简单反向代理配置:
server { listen 80; server_name example.com; location / { proxy_pass http://127.0.0.1:3000; } }带路径的反向代理:
server { listen 80; server_name example.com; location /api/ { proxy_pass http://127.0.0.1:3000/api/; } }不带路径的反向代理:
server { listen 80; server_name example.com; location /api { proxy_pass http://127.0.0.1:3000; } }
注意事项:
- 如果
proxy_pass的URL包含路径,那么Nginx会将location匹配的路径替换为该URL路径 - 如果
proxy_pass的URL不包含路径,那么Nginx会将完整的请求URL(包括location匹配的路径)转发给后端服务器
4.1.3 请求头转发与修改
Nginx默认会转发一些基本的HTTP请求头,但在实际应用中,我们可能需要自定义或修改请求头。
常用的代理头配置:
location / {
proxy_pass http://backend_server;
# 转发真实客户端IP地址
proxy_set_header X-Real-IP $remote_addr;
# 转发客户端IP地址列表(如果经过多级代理)
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
# 转发原始请求协议(http或https)
proxy_set_header X-Forwarded-Proto $scheme;
# 转发原始请求主机名
proxy_set_header Host $host;
# 设置连接超时时间
proxy_connect_timeout 60s;
# 设置读取超时时间
proxy_read_timeout 60s;
# 设置发送超时时间
proxy_send_timeout 60s;
}请求头说明:
X-Real-IP:记录真实客户端IP地址X-Forwarded-For:记录客户端IP地址列表,格式为client, proxy1, proxy2X-Forwarded-Proto:记录原始请求的协议(http或https)Host:记录原始请求的主机名
4.2 负载均衡策略
负载均衡(Load Balancing)是指将客户端请求分发到多个后端服务器,以提高系统的可用性和性能。
4.2.1 轮询(round-robin)
轮询是Nginx默认的负载均衡策略,将请求按顺序分发到每个后端服务器。
配置示例:
# 定义后端服务器组
upstream backend {
server 127.0.0.1:3000;
server 127.0.0.1:3001;
server 127.0.0.1:3002;
}
server {
listen 80;
server_name example.com;
location / {
proxy_pass http://backend;
# 添加请求头配置
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header Host $host;
}
}4.2.2 最少连接(least_conn)
最少连接策略将请求分发到当前活跃连接数最少的后端服务器,适合处理请求时间差异较大的场景。
配置示例:
upstream backend {
least_conn;
server 127.0.0.1:3000;
server 127.0.0.1:3001;
server 127.0.0.1:3002;
}4.2.3 IP哈希(ip_hash)
IP哈希策略根据客户端IP地址的哈希值将请求分发到固定的后端服务器,适合需要会话保持的场景。
配置示例:
upstream backend {
ip_hash;
server 127.0.0.1:3000;
server 127.0.0.1:3001;
server 127.0.0.1:3002;
}4.2.4 权重配置
权重配置允许为每个后端服务器分配不同的权重,权重越高,接收的请求越多。
配置示例:
upstream backend {
server 127.0.0.1:3000 weight=3;
server 127.0.0.1:3001 weight=2;
server 127.0.0.1:3002 weight=1;
}权重说明:
- 上述配置中,3000端口的服务器接收3/6的请求,3001端口的服务器接收2/6的请求,3002端口的服务器接收1/6的请求
- 权重可以是任意正整数,默认权重为1
4.3 健康检查与故障转移
健康检查用于监控后端服务器的运行状态,当服务器出现故障时,自动将请求转发到其他健康的服务器。
4.3.1 被动健康检查
被动健康检查是Nginx默认的健康检查方式,通过监控后端服务器的响应状态来判断服务器是否健康。
配置示例:
upstream backend {
server 127.0.0.1:3000 max_fails=3 fail_timeout=30s;
server 127.0.0.1:3001 max_fails=3 fail_timeout=30s;
server 127.0.0.1:3002 max_fails=3 fail_timeout=30s;
}指令说明:
max_fails:在fail_timeout时间内,最大失败次数,默认为1fail_timeout:失败超时时间,默认为10秒
工作原理:
- 当Nginx向后端服务器发送请求失败时,计数器加1
- 如果在
fail_timeout时间内,失败次数达到max_fails,则将该服务器标记为不可用 - 在
fail_timeout时间后,Nginx会再次尝试向该服务器发送请求,如果成功,则将其标记为可用
4.3.2 主动健康检查(商业版)
主动健康检查是Nginx Plus(商业版)的功能,允许定期向后端服务器发送请求,主动检查服务器的健康状态。
配置示例:
upstream backend {
zone backend 64k;
server 127.0.0.1:3000;
server 127.0.0.1:3001;
server 127.0.0.1:3002;
health_check interval=5s fails=2 passes=2;
}指令说明:
interval:检查间隔时间,默认为5秒fails:连续失败次数,默认为1passes:连续成功次数,默认为1
开源替代方案:
- 使用
ngx_http_upstream_check_module模块,需要重新编译Nginx - 使用外部健康检查工具,如Consul、ZooKeeper等
4.4 实战项目2:为Node.js/Python应用配置反向代理
在这个实战项目中,我们将为Node.js和Python应用配置Nginx反向代理和负载均衡。
4.4.1 项目准备
1. 安装Node.js和Python
# Ubuntu/Debian
curl -fsSL https://deb.nodesource.com/setup_18.x | sudo -E bash -
sudo apt-get install -y nodejs python3 python3-pip
# CentOS/RHEL
sudo yum install -y nodejs python3 python3-pip2. 创建Node.js应用
# 创建Node.js应用目录
mkdir -p ~/apps/node-app
cd ~/apps/node-app
# 初始化package.json
npm init -y
# 安装Express框架
npm install express
# 创建app.js文件
cat > app.js << EOF
const express = require('express');
const app = express();
const port = process.env.PORT || 3000;
app.get('/', (req, res) => {
res.send(`Hello from Node.js app running on port ${port}!`);
});
app.listen(port, () => {
console.log(`Node.js app listening at http://localhost:${port}`);
});
EOF3. 创建Python应用
# 创建Python应用目录
mkdir -p ~/apps/python-app
cd ~/apps/python-app
# 创建requirements.txt
cat > requirements.txt << EOF
Flask
EOF
# 安装依赖
pip3 install -r requirements.txt
# 创建app.py文件
cat > app.py << EOF
from flask import Flask
import os
app = Flask(__name__)
port = os.environ.get('PORT', 5000)
@app.route('/')
def hello():
return f'Hello from Python app running on port {port}!'
if __name__ == '__main__':
app.run(host='0.0.0.0', port=port)
EOF4.4.2 启动应用
1. 启动Node.js应用(3个实例)
# 启动第一个实例(端口3000)
cd ~/apps/node-app
PORT=3000 node app.js &
# 启动第二个实例(端口3001)
PORT=3001 node app.js &
# 启动第三个实例(端口3002)
PORT=3002 node app.js &2. 启动Python应用(2个实例)
# 启动第一个实例(端口5000)
cd ~/apps/python-app
PORT=5000 python3 app.py &
# 启动第二个实例(端口5001)
PORT=5001 python3 app.py &3. 验证应用是否正常运行
# 测试Node.js应用
curl http://localhost:3000
curl http://localhost:3001
curl http://localhost:3002
# 测试Python应用
curl http://localhost:5000
curl http://localhost:50014.4.3 配置Nginx反向代理
创建Nginx配置文件:
sudo nano /etc/nginx/conf.d/app-proxy.conf添加以下配置:
# 定义Node.js应用服务器组 upstream node_app { server 127.0.0.1:3000 max_fails=3 fail_timeout=30s; server 127.0.0.1:3001 max_fails=3 fail_timeout=30s; server 127.0.0.1:3002 max_fails=3 fail_timeout=30s; } # 定义Python应用服务器组 upstream python_app { server 127.0.0.1:5000 max_fails=3 fail_timeout=30s; server 127.0.0.1:5001 max_fails=3 fail_timeout=30s; } server { listen 80; server_name app.example.com; # Node.js应用反向代理 location /node/ { proxy_pass http://node_app/; 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_set_header Host $host; } # Python应用反向代理 location /python/ { proxy_pass http://python_app/; 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_set_header Host $host; } # 根路径重定向 location / { return 302 /node/; } }验证配置并重载:
sudo nginx -t sudo systemctl reload nginx本地测试配置:
sudo nano /etc/hosts添加:
127.0.0.1 app.example.com访问应用:
- 通过浏览器访问:
- Node.js应用:http://app.example.com/node/
- Python应用:http://app.example.com/python/
- 或通过命令行测试:
# 测试Node.js应用(多次访问,观察负载均衡效果) curl http://app.example.com/node/ curl http://app.example.com/node/ curl http://app.example.com/node/ # 测试Python应用(多次访问,观察负载均衡效果) curl http://app.example.com/python/ curl http://app.example.com/python/
- 通过浏览器访问:
4.4.4 测试负载均衡和故障转移
1. 测试负载均衡
# 连续访问Node.js应用,观察响应中的端口号变化
for i in {1..10}; do curl http://app.example.com/node/; echo; done预期输出:
Hello from Node.js app running on port 3000!
Hello from Node.js app running on port 3001!
Hello from Node.js app running on port 3002!
Hello from Node.js app running on port 3000!
Hello from Node.js app running on port 3001!
Hello from Node.js app running on port 3002!
Hello from Node.js app running on port 3000!
Hello from Node.js app running on port 3001!
Hello from Node.js app running on port 3002!
Hello from Node.js app running on port 3000!2. 测试故障转移
# 停止一个Node.js实例
pkill -f "node app.js.*3000"
# 连续访问Node.js应用,观察响应中的端口号变化
for i in {1..10}; do curl http://app.example.com/node/; echo; done预期输出:
- 初始几次访问可能会失败
- 随后的访问只会分发到端口3001和3002的实例
- 不会再分发到已停止的3000端口实例
3. 恢复故障实例
# 重新启动Node.js实例
cd ~/apps/node-app
PORT=3000 node app.js &
# 等待一段时间后,测试是否恢复
for i in {1..10}; do curl http://app.example.com/node/; echo; done预期输出:
- 所有三个实例都恢复正常,请求会再次分发到3000端口实例
4.4.5 常见问题与解决方案
问题1:502 Bad Gateway错误
解决方案:
检查后端服务器是否正常运行:
curl http://localhost:3000检查Nginx配置中的
proxy_pass指令是否正确检查防火墙设置,确保Nginx可以访问后端服务器端口:
sudo ufw status sudo ufw allow 3000/tcp
问题2:应用无法获取真实客户端IP
解决方案:
确保Nginx配置中添加了以下请求头:
proxy_set_header X-Real-IP $remote_addr; proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;在应用中读取这些请求头,而不是直接获取连接IP
问题3:负载均衡不均匀
解决方案:
- 确保所有后端服务器的配置和性能相近
- 调整权重配置,根据服务器性能分配不同的权重
- 考虑使用其他负载均衡策略,如
least_conn或ip_hash
章节总结
在本章中,我们学习了:
反向代理基础:
- 反向代理的概念和优势
proxy_pass指令的使用- 请求头转发与修改
负载均衡策略:
- 轮询(round-robin)
- 最少连接(least_conn)
- IP哈希(ip_hash)
- 权重配置
健康检查与故障转移:
- 被动健康检查
- 主动健康检查(商业版)
- 故障转移机制
实战项目:
- 创建Node.js和Python应用
- 配置Nginx反向代理和负载均衡
- 测试负载均衡和故障转移
实践练习
- 为两个Node.js应用配置Nginx反向代理
- 实现轮询、最少连接和IP哈希三种负载均衡策略
- 测试故障转移功能
- 配置自定义请求头转发
- 为应用添加SSL/TLS加密
延伸阅读
- Nginx Reverse Proxy Documentation
- Nginx Load Balancing Documentation
- Node.js Express Documentation
- Flask Documentation
下一章:第5章:安全与性能