第2章 - 负载均衡原理
嗨,朋友!
你有没有想过,像淘宝、微信这样的大型网站,每天要处理数亿次请求,一台服务器怎么可能扛得住?答案就是负载均衡(Load Balance)!
今天我们要学习的,就是如何通过负载均衡技术,让多台服务器协同工作,提高系统的性能和可用性。这是后端开发和运维工程师必须掌握的核心技能。
🤔 什么是负载均衡?
生活中的类比
想象一下,你去银行办业务:
没有负载均衡:
客户1 →
客户2 → 唯一的柜台(忙不过来)
客户3 →
客户4 → (排队很长) 😫
有了负载均衡:
┌→ 柜台1
客户 → 大堂经理 ├→ 柜台2 (每个柜台都不太忙)
└→ 柜台3
✅ 效率高
大堂经理就是负载均衡器,他会把客户分配给不同的柜台,让每个柜台的工作量都比较均衡。
正式定义
定义
负载均衡是一种计算机网络技术,用来将网络流量分配到多个服务器上,从而提高应用的响应速度和可用性。
核心作用:
- 📈 提高性能:多台服务器并行处理请求
- 🛡️ 提高可用性:一台服务器挂了,其他服务器继续工作
- 🔄 方便扩展:随时可以添加或减少服务器
🏗️ 负载均衡的类型
根据工作的网络层次,负载均衡可以分为几种类型:
1. 四层负载均衡(传输层)
工作在 TCP/UDP 层,只根据 IP 和端口进行转发。
客户端 → 负载均衡器(查看 IP:Port)→ 后端服务器
(不解析 HTTP 内容)
特点:
- ✅ 速度快(不需要解析应用层数据)
- ✅ 消耗资源少
- ❌ 功能相对简单
- ❌ 无法根据 URL、Cookie 等进行分发
典型代表:
- LVS(Linux Virtual Server)
- F5 硬件负载均衡器
- HAProxy(也支持七层)
2. 七层负载均衡(应用层)
工作在 HTTP/HTTPS 层,可以根据请求内容进行转发。
客户端 → 负载均衡器(解析 HTTP 请求)→ 后端服务器
(可以查看 URL、Header 等)
特点:
- ✅ 功能强大(可以根据 URL、Cookie、Header 等分发)
- ✅ 可以做内容缓存、压缩等
- ✅ 可以实现更复杂的路由策略
- ❌ 速度相对较慢
- ❌ 消耗资源较多
典型代表:
- Nginx
- Apache HTTP Server
- HAProxy
- AWS ELB/ALB
对比表格
| 对比项 | 四层负载均衡 | 七层负载均衡 |
|---|---|---|
| 工作层次 | 传输层(TCP/UDP) | 应用层(HTTP/HTTPS) |
| 转发依据 | IP + 端口 | URL、Header、Cookie 等 |
| 性能 | 非常快 | 相对较慢 |
| 功能 | 简单 | 丰富 |
| 应用场景 | 高并发、低延迟 | 需要内容路由 |
| 典型代表 | LVS、F5 | Nginx、HAProxy |
🎯 负载均衡算法
负载均衡器如何决定把请求发给哪台服务器呢?这就需要负载均衡算法。
1. 轮询(Round Robin)
原理:依次将请求分配给每台服务器。
请求1 → 服务器1
请求2 → 服务器2
请求3 → 服务器3
请求4 → 服务器1 (循环)
请求5 → 服务器2
优点:
- ✅ 简单易实现
- ✅ 请求分配均匀
缺点:
- ❌ 不考虑服务器的实际负载
- ❌ 不考虑服务器的性能差异
适用场景:服务器性能相近,请求处理时间差不多
2. 加权轮询(Weighted Round Robin)
原理:给每台服务器分配一个权重,权重越高,分配的请求越多。
服务器1(权重3)
服务器2(权重2)
服务器3(权重1)
分配:1,1,1, 2,2, 3 (循环)
优点:
- ✅ 可以根据服务器性能分配
- ✅ 灵活性更好
适用场景:服务器性能不同
3. 最少连接(Least Connections)
原理:把新请求分配给当前连接数最少的服务器。
服务器1:5个连接
服务器2:3个连接 ← 新请求分配到这里
服务器3:8个连接
优点:
- ✅ 考虑了服务器的实际负载
- ✅ 适合长连接场景
缺点:
- ❌ 需要维护连接计数
- ❌ 实现稍复杂
适用场景:请求处理时间差异较大的场景
4. IP 哈希(IP Hash)
原理:根据客户端 IP 地址计算哈希值,相同 IP 的请求总是发往同一台服务器。
IP: 192.168.1.100 → hash() → 服务器2
IP: 192.168.1.101 → hash() → 服务器1
IP: 192.168.1.100 → hash() → 服务器2 (同一台)
优点:
- ✅ 保证同一用户的请求到同一服务器
- ✅ 适合有状态的应用(Session 保持)
缺点:
- ❌ 服务器数量变化时,哈希结果会改变
- ❌ 可能导致负载不均
适用场景:需要 Session 保持的场景
5. URL 哈希(URL Hash)
原理:根据请求的 URL 计算哈希值,相同 URL 的请求发往同一台服务器。
/api/users → 服务器1
/api/orders → 服务器2
/api/users → 服务器1 (缓存命中率高)
优点:
- ✅ 提高缓存命中率
- ✅ 适合静态资源
适用场景:CDN、静态资源服务器
6. 最快响应时间(Least Response Time)
原理:选择响应时间最短的服务器。
优点:
- ✅ 考虑了服务器的实际性能
- ✅ 用户体验好
缺点:
- ❌ 需要实时监控响应时间
- ❌ 实现复杂
💻 Nginx 负载均衡实战
Nginx 是最流行的七层负载均衡器,下面我们来看实际配置。
基础配置 - 轮询
http {
# 定义后端服务器组
upstream backend {
server 192.168.1.101:8080;
server 192.168.1.102:8080;
server 192.168.1.103:8080;
}
server {
listen 80;
server_name example.com;
location / {
# 代理到后端服务器组
proxy_pass http://backend;
# 传递客户端真实 IP
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
}
}
}
加权轮询
upstream backend {
server 192.168.1.101:8080 weight=3; # 性能好,权重高
server 192.168.1.102:8080 weight=2;
server 192.168.1.103:8080 weight=1; # 性能差,权重低
}
IP 哈希
upstream backend {
ip_hash; # 启用 IP 哈希算法
server 192.168.1.101:8080;
server 192.168.1.102:8080;
server 192.168.1.103:8080;
}
最少连接
upstream backend {
least_conn; # 启用最少连接算法
server 192.168.1.101:8080;
server 192.168.1.102:8080;
server 192.168.1.103:8080;
}
高级配置 - 健康检查
upstream backend {
server 192.168.1.101:8080 max_fails=3 fail_timeout=30s;
server 192.168.1.102:8080 max_fails=3 fail_timeout=30s;
server 192.168.1.103:8080 max_fails=3 fail_timeout=30s;
# max_fails: 失败3次后标记为不可用
# fail_timeout: 30秒后重新尝试
}
完整的生产环境配置
http {
# 定义后端服务器组
upstream backend {
least_conn; # 最少连接算法
server 192.168.1.101:8080 weight=3 max_fails=3 fail_timeout=30s;
server 192.168.1.102:8080 weight=2 max_fails=3 fail_timeout=30s;
server 192.168.1.103:8080 weight=1 max_fails=3 fail_timeout=30s;
# 备用服务器(只有主服务器都挂了才启用)
server 192.168.1.104:8080 backup;
# 长连接配置
keepalive 32; # 保持32个长连接
}
server {
listen 80;
server_name api.example.com;
# 日志配置
access_log /var/log/nginx/api_access.log;
error_log /var/log/nginx/api_error.log;
location / {
proxy_pass http://backend;
# HTTP 头配置
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 60s;
proxy_send_timeout 60s;
proxy_read_timeout 60s;
# 长连接配置
proxy_http_version 1.1;
proxy_set_header Connection "";
}
# 健康检查接口
location /health {
access_log off;
return 200 "OK";
}
}
}
🔍 健康检查机制
负载均衡器需要知道哪些服务器是健康的,哪些是故障的。
主动健康检查
负载均衡器定期向后端服务器发送请求,检查是否正常。
upstream backend {
server 192.168.1.101:8080;
# Nginx Plus 支持主动健康检查
# 开源版需要第三方模块
}
location / {
proxy_pass http://backend;
# 健康检查配置(Nginx Plus)
health_check interval=5s fails=3 passes=2;
}
参数说明:
interval=5s:每5秒检查一次fails=3:失败3次标记为不健康passes=2:成功2次标记为健康
被动健康检查
根据实际请求的响应情况判断服务器健康状态。
upstream backend {
server 192.168.1.101:8080 max_fails=3 fail_timeout=30s;
# max_fails: 在 fail_timeout 时间内失败3次就标记为不可用
# fail_timeout: 标记为不可用后,30秒后重新尝试
}
自定义健康检查接口
在后端服务中实现健康检查接口:
// Node.js 示例
const express = require('express');
const app = express();
// 健康检查接口
app.get('/health', (req, res) => {
// 检查数据库连接
// 检查依赖服务
// 检查系统资源
const healthy = checkDatabaseConnection() &&
checkExternalServices() &&
checkSystemResources();
if (healthy) {
res.status(200).json({ status: 'UP' });
} else {
res.status(503).json({ status: 'DOWN' });
}
});
function checkDatabaseConnection() {
// 实际检查数据库连接
return true;
}
function checkExternalServices() {
// 检查依赖的外部服务
return true;
}
function checkSystemResources() {
// 检查 CPU、内存等资源
const memUsage = process.memoryUsage();
const maxMemory = 1024 * 1024 * 1024; // 1GB
return memUsage.heapUsed < maxMemory * 0.9;
}
app.listen(8080);
🌐 实际应用场景
场景1:Web 应用负载均衡
┌→ Web服务器1
用户 → Nginx(LB) ─┼→ Web服务器2
└→ Web服务器3
↓
数据库服务器
场景2:微服务架构
客户端 → API网关(负载均衡) ┬→ 用户服务(3个实例)
├→ 订单服务(3个实例)
├→ 商品服务(3个实例)
└→ 支付服务(3个实例)
场景3:多层负载均衡
DNS轮询
↓
┌────────┴────────┐
↓ ↓
机房A(Nginx) 机房B(Nginx)
↓ ↓
服务器组1 服务器组2
🛠️ 负载均衡器选型
开源方案对比
| 产品 | 类型 | 性能 | 易用性 | 功能 | 适用场景 |
|---|---|---|---|---|---|
| Nginx | 4/7层 | 很高 | 中等 | 丰富 | Web应用、API网关 |
| HAProxy | 4/7层 | 极高 | 较难 | 丰富 | 高并发、TCP代理 |
| LVS | 4层 | 极高 | 难 | 基础 | 超高并发、入口级 |
| Traefik | 7层 | 中等 | 简单 | 现代化 | 微服务、容器 |
云服务方案
| 云服务商 | 产品 | 特点 |
|---|---|---|
| AWS | ELB/ALB/NLB | 功能最全,价格适中 |
| 阿里云 | SLB | 性价比高 |
| 腾讯云 | CLB | 与腾讯生态集成好 |
| Azure | Azure LB | 适合微软技术栈 |
📊 性能优化技巧
1. 启用长连接
upstream backend {
server 192.168.1.101:8080;
keepalive 32; # 保持32个长连接
}
location / {
proxy_pass http://backend;
proxy_http_version 1.1; # 使用 HTTP/1.1
proxy_set_header Connection ""; # 清除 Connection 头
}
2. 启用缓存
http {
# 定义缓存路径
proxy_cache_path /var/cache/nginx levels=1:2 keys_zone=my_cache:10m max_size=1g inactive=60m;
server {
location / {
proxy_cache my_cache;
proxy_cache_valid 200 60m; # 200响应缓存60分钟
proxy_cache_valid 404 10m; # 404响应缓存10分钟
proxy_pass http://backend;
}
}
}
3. 启用压缩
http {
gzip on;
gzip_types text/plain text/css application/json application/javascript;
gzip_min_length 1000;
gzip_comp_level 6;
}
4. 限流保护
http {
# 定义限流区域
limit_req_zone $binary_remote_addr zone=mylimit:10m rate=10r/s;
server {
location /api/ {
# 限制每秒10个请求,突发20个
limit_req zone=mylimit burst=20 nodelay;
proxy_pass http://backend;
}
}
}
🔧 常见问题与解决方案
问题1:Session 保持
问题:用户的请求被分配到不同服务器,Session 丢失。
解决方案:
- 使用 IP 哈希:
upstream backend {
ip_hash;
server 192.168.1.101:8080;
server 192.168.1.102:8080;
}
- Session 共享(推荐):
// 使用 Redis 存储 Session
const session = require('express-session');
const RedisStore = require('connect-redis')(session);
app.use(session({
store: new RedisStore({
host: 'redis-server',
port: 6379
}),
secret: 'your-secret-key',
resave: false,
saveUninitialized: false
}));
- 使用 JWT(无状态):
const jwt = require('jsonwebtoken');
// 登录时生成 Token
const token = jwt.sign({ userId: 123 }, 'secret', { expiresIn: '1h' });
// 后续请求携带 Token,无需 Session
问题2:后端服务器获取真实客户端 IP
Nginx 配置:
location / {
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_pass http://backend;
}
后端代码:
// Node.js
app.get('/', (req, res) => {
const clientIP = req.headers['x-real-ip'] ||
req.headers['x-forwarded-for'] ||
req.connection.remoteAddress;
console.log('客户端 IP:', clientIP);
});
问题3:WebSocket 负载均衡
http {
map $http_upgrade $connection_upgrade {
default upgrade;
'' close;
}
upstream websocket {
ip_hash; # WebSocket 需要保持连接到同一服务器
server 192.168.1.101:8080;
server 192.168.1.102:8080;
}
server {
location /ws/ {
proxy_pass http://websocket;
# WebSocket 特殊配置
proxy_http_version 1.1;
proxy_set_header Upgrade $http_upgrade;
proxy_set_header Connection $connection_upgrade;
proxy_set_header Host $host;
# 超时配置(WebSocket 连接可能很长)
proxy_read_timeout 3600s;
proxy_send_timeout 3600s;
}
}
}
📈 监控与日志
Nginx 状态监控
server {
listen 8080;
location /nginx_status {
stub_status on;
access_log off;
allow 127.0.0.1; # 只允许本地访问
deny all;
}
}
访问 http://localhost:8080/nginx_status 查看:
Active connections: 291
server accepts handled requests
16630948 16630948 31070465
Reading: 6 Writing: 179 Waiting: 106
详细日志格式
http {
log_format main '$remote_addr - $remote_user [$time_local] '
'"$request" $status $body_bytes_sent '
'"$http_referer" "$http_user_agent" '
'$upstream_addr $upstream_status $upstream_response_time $request_time';
access_log /var/log/nginx/access.log main;
}
💡 学习建议
1. 从简单开始 🚀
先搭建一个简单的 Nginx 负载均衡环境,体验轮询算法的效果。
2. 理解不同算法的适用场景 🎯
- 性能相近的服务器 → 轮询
- 性能不同的服务器 → 加权轮询
- 长连接场景 → 最少连接
- 需要 Session 保持 → IP 哈希
3. 关注健康检查 ❤️
健康检查是保证高可用的关键,一定要配置合理的健康检查策略。
4. 监控和日志 📊
生产环境一定要配置完善的监控和日志,及时发现问题。
📝 练习题
基础题
- 四层负载均衡和七层负载均衡有什么区别?各自适用于什么场景?
点击查看答案
四层负载均衡:
- 工作在传输层(TCP/UDP)
- 只根据 IP 和端口转发
- 速度快,资源消耗少
- 功能简单
- 适合高并发、低延迟场景
七层负载均衡:
- 工作在应用层(HTTP/HTTPS)
- 可以根据 URL、Header、Cookie 等转发
- 功能丰富,可以做内容路由、缓存等
- 速度相对较慢,资源消耗较多
- 适合需要复杂路由策略的场景
- Nginx 负载均衡支持哪些算法?
点击查看答案
- 轮询(round-robin):默认算法,依次分配
- 加权轮询(weighted round-robin):根据权重分配
- IP 哈希(ip_hash):根据客户端 IP 分配
- 最少连接(least_conn):分配给连接数最少的服务器
- URL 哈希(hash):根据 URL 或其他变量分配
进阶题
- 如何解决负载均衡环境下的 Session 保持问题?列举至少三种方案。
点击查看答案
方案1:IP 哈希
upstream backend {
ip_hash;
server 192.168.1.101:8080;
}
优点:配置简单 缺点:负载可能不均衡,服务器变化会影响分配
方案2:Session 共享(推荐) 使用 Redis、Memcached 等集中存储 Session 优点:灵活,负载均衡 缺点:需要额外的存储服务
方案3:无状态设计(JWT) 使用 Token 而不是 Session 优点:完全无状态,易于扩展 缺点:Token 较大,无法主动失效
方案4:Sticky Session
upstream backend {
sticky cookie srv_id expires=1h;
server 192.168.1.101:8080;
}
(需要 Nginx Plus 或第三方模块)
- 编写一个 Nginx 配置,实现以下需求:
- 对
/api/开头的请求做负载均衡 - 使用加权轮询算法
- 配置健康检查
- 获取客户端真实 IP
- 对
点击查看答案
http {
upstream api_backend {
# 加权轮询
server 192.168.1.101:8080 weight=3 max_fails=3 fail_timeout=30s;
server 192.168.1.102:8080 weight=2 max_fails=3 fail_timeout=30s;
server 192.168.1.103:8080 weight=1 max_fails=3 fail_timeout=30s;
# 备用服务器
server 192.168.1.104:8080 backup;
# 长连接
keepalive 32;
}
server {
listen 80;
server_name api.example.com;
location /api/ {
proxy_pass http://api_backend;
# 传递真实 IP
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 10s;
proxy_send_timeout 60s;
proxy_read_timeout 60s;
# 长连接
proxy_http_version 1.1;
proxy_set_header Connection "";
}
# 健康检查接口
location /health {
access_log off;
return 200 "OK";
}
}
}
🎓 下一步学习
恭喜你!现在你已经掌握了负载均衡的核心概念和实践技巧。
建议继续学习:
- 第3章 - CDN 内容分发网络 - 了解全局负载均衡
- 第1章 - TCP 三次握手与四次挥手 - 深入理解连接机制
- 实战案例 - 运维中的网络配置 - 实际部署经验
推荐阅读:
- Nginx 官方文档
- 《深入理解 Nginx》
- HAProxy 官方文档
关键概念回顾:
- ✅ 负载均衡可以提高性能、可用性和可扩展性
- ✅ 四层负载均衡快但功能简单,七层负载均衡功能丰富但相对慢
- ✅ 常用算法:轮询、加权轮询、最少连接、IP 哈希
- ✅ 健康检查是保证高可用的关键
- ✅ Session 保持可以通过 IP 哈希、Session 共享、JWT 等方式解决
