跳到主要内容

负载均衡

负载均衡是将网络请求分发到多个服务器的技术。本章将介绍如何使用 Nginx 实现负载均衡。

负载均衡概述

什么是负载均衡?

负载均衡(Load Balancing)是一种将工作负载分布到多个服务器上的技术,目的是:

  • 提高性能:并行处理更多请求
  • 提高可用性:一台服务器故障,其他服务器继续服务
  • 提高扩展性:可以动态添加服务器

Nginx 负载均衡的优势

  1. 配置简单:几行配置即可实现
  2. 性能优异:高并发处理能力
  3. 多种策略:支持多种负载均衡算法
  4. 健康检查:自动检测后端服务器状态
  5. 会话保持:支持基于 Cookie 的会话粘滞

upstream 模块

基本配置

# 定义后端服务器组
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;
}
}

解释

  • upstream 定义后端服务器组
  • server 指定每台后端服务器的地址和端口
  • proxy_pass 引用 upstream 组

负载均衡策略

1. 轮询(Round Robin)

默认策略,按顺序分发请求:

upstream backend {
server 192.168.1.101:8080;
server 192.168.1.102:8080;
server 192.168.1.103:8080;
}

工作流程

请求1 → 服务器1
请求2 → 服务器2
请求3 → 服务器3
请求4 → 服务器1
...

2. 加权轮询(Weighted Round Robin)

根据权重分发请求,权重越高,分到的请求越多:

upstream backend {
server 192.168.1.101:8080 weight=5; # 处理 5/8 的请求
server 192.168.1.102:8080 weight=2; # 处理 2/8 的请求
server 192.168.1.103:8080 weight=1; # 处理 1/8 的请求
}

解释

  • 适用于服务器性能不同的情况
  • 高配置服务器设置较高权重

3. IP Hash

根据客户端 IP 地址的哈希值分配服务器,保证同一客户端总是访问同一服务器:

upstream backend {
ip_hash;
server 192.168.1.101:8080;
server 192.168.1.102:8080;
server 192.168.1.103:8080;
}

解释

  • 适合需要会话保持的应用
  • 同一 IP 的请求总是发送到同一后端
  • 如果后端服务器变化,可能会重新分配

4. 最少连接(Least Connections)

将请求发送到当前连接数最少的服务器:

upstream backend {
least_conn;
server 192.168.1.101:8080;
server 192.168.1.102:8080;
server 192.168.1.103:8080;
}

解释

  • 适合请求处理时间差异较大的场景
  • 自动将请求发送到最空闲的服务器

5. 一致性哈希(需要第三方模块)

根据请求的某个特征进行哈希:

# 需要安装 ngx_http_upstream_hash_module
upstream backend {
hash $request_uri consistent;
server 192.168.1.101:8080;
server 192.168.1.102:8080;
server 192.168.1.103:8080;
}

策略对比

策略适用场景特点
轮询服务器性能相同简单公平
加权轮询服务器性能不同按能力分配
IP Hash需要会话保持客户端绑定
最少连接请求时间差异大动态负载

服务器参数

weight(权重)

upstream backend {
server 192.168.1.101:8080 weight=3;
server 192.168.1.102:8080 weight=1;
}

max_fails 和 fail_timeout

upstream backend {
# max_fails: 最大失败次数
# fail_timeout: 失败后的超时时间
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 时间内失败 max_fails 次,则标记为不可用
  • fail_timeout 后,再次尝试连接

backup(备用服务器)

upstream backend {
server 192.168.1.101:8080;
server 192.168.1.102:8080;
server 192.168.1.103:8080 backup; # 备用服务器
}

解释

  • backup 服务器只在其他服务器都不可用时使用
  • 适合用于故障转移

down(标记下线)

upstream backend {
server 192.168.1.101:8080;
server 192.168.1.102:8080 down; # 永久下线
server 192.168.1.103:8080;
}

健康检查

被动健康检查

Nginx 通过响应判断后端健康状态:

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;
}

主动健康检查(商业版)

Nginx Plus 支持主动健康检查:

upstream backend {
zone backend 64k;
server 192.168.1.101:8080;
server 192.168.1.102:8080;

# 主动健康检查
health_check interval=5s fails=3 passes=2 uri=/health match=status_ok;
}

match status_ok {
status 200;
header Content-Type ~ "application/json";
body ~ '"status":"ok"';
}

开源版主动健康检查

可以使用第三方模块实现:

# 安装 nginx_upstream_check_module
# https://github.com/yaoweibin/nginx_upstream_check_module
upstream backend {
server 192.168.1.101:8080;
server 192.168.1.102:8080;

check interval=3000 rise=2 fall=5 timeout=1000 type=http;
check_http_send "HEAD /health HTTP/1.1\r\n\r\n";
check_http_expect_alive http_2xx http_3xx;
}

会话保持

基于 IP 的会话保持

使用 ip_hash 策略:

upstream backend {
ip_hash;
server 192.168.1.101:8080;
server 192.168.1.102:8080;
}
upstream backend {
server 192.168.1.101:8080;
server 192.168.1.102:8080;

# 使用 sticky cookie(需要 nginx-sticky-module)
# sticky cookie srv_id expires=1h;
}

# 或通过应用层实现
server {
location / {
proxy_pass http://backend;
proxy_set_header X-Session-ID $cookie_sessionid;
}
}

基于学习模式(Nginx Plus)

upstream backend {
server 192.168.1.101:8080;
server 192.168.1.102:8080;

sticky learn
create=$upstream_cookie_sessionid
lookup=$cookie_sessionid
zone=client_sessions:1m;
}

高级配置

限制连接数

upstream backend {
server 192.168.1.101:8080 max_conns=100; # 最大连接数
server 192.168.1.102:8080 max_conns=100;
}

慢启动

upstream backend {
server 192.168.1.101:8080 slow_start=30s; # 30秒内逐渐增加流量
server 192.168.1.102:8080;
}

队列配置

upstream backend {
server 192.168.1.101:8080;
server 192.168.1.102:8080;

# 所有服务器繁忙时,排队等待
queue 100 timeout=60s;
}

实际场景配置

场景 1:Web 应用负载均衡

upstream web_cluster {
least_conn;
server 192.168.1.101:8080 weight=3;
server 192.168.1.102:8080 weight=3;
server 192.168.1.103:8080 weight=1;

keepalive 32;
}

server {
listen 80;
server_name example.com;

location / {
proxy_pass http://web_cluster;
proxy_http_version 1.1;
proxy_set_header Connection "";
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
}
}

场景 2:API 服务器负载均衡

upstream api_cluster {
ip_hash; # 保持会话
server 192.168.1.101:3000 max_fails=3 fail_timeout=30s;
server 192.168.1.102:3000 max_fails=3 fail_timeout=30s;
server 192.168.1.103:3000 backup;
}

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

location / {
proxy_pass http://api_cluster;
proxy_connect_timeout 5s;
proxy_read_timeout 30s;
proxy_next_upstream error timeout http_503;
}
}

场景 3:数据库读负载均衡

upstream mysql_read {
least_conn;
server 192.168.1.101:3306 weight=5;
server 192.168.1.102:3306 weight=3;
server 192.168.1.103:3306 weight=1;
}

# 注意:Nginx 不直接代理 MySQL
# 这里仅作示例,实际使用需要专门的数据库代理

场景 4:灰度发布

upstream stable {
server 192.168.1.101:8080;
server 192.168.1.102:8080;
}

upstream canary {
server 192.168.1.103:8080;
}

server {
listen 80;
server_name example.com;

# 根据 Cookie 分流
set $backend "stable";
if ($cookie_canary = "true") {
set $backend "canary";
}

# 根据 Header 分流
if ($http_x_canary = "true") {
set $backend "canary";
}

# 按比例分流(10% 流量到新版本)
split_clients "${remote_addr}" $variant {
10% canary;
* stable;
}

location / {
proxy_pass http://$variant;
proxy_set_header Host $host;
}
}

监控和调试

状态页面

server {
listen 80;
server_name localhost;

location /upstream_status {
stub_status on;
allow 127.0.0.1;
deny all;
}
}

日志配置

log_format upstream '$remote_addr - $remote_user [$time_local] '
'"$request" $status $body_bytes_sent '
'"$http_referer" "$http_user_agent" '
'upstream=$upstream_addr '
'response_time=$upstream_response_time '
'status=$upstream_status';

server {
access_log /var/log/nginx/upstream.log upstream;
}

添加调试头

server {
add_header X-Upstream-Addr $upstream_addr;
add_header X-Upstream-Status $upstream_status;
add_header X-Upstream-Response-Time $upstream_response_time;

location / {
proxy_pass http://backend;
}
}

小结

本章我们学习了:

  1. 负载均衡的概念和优势
  2. upstream 模块的配置
  3. 多种负载均衡策略(轮询、加权、IP Hash、最少连接)
  4. 服务器参数配置(weight、max_fails、backup)
  5. 健康检查机制
  6. 会话保持方法
  7. 实际场景配置示例
  8. 监控和调试

练习

  1. 配置一个基本的轮询负载均衡
  2. 配置加权负载均衡
  3. 配置 IP Hash 会话保持
  4. 实现灰度发布配置

延伸阅读