Skip to main content

Nginx负载均衡简单实验

实验环境

docker版本 Nginx1.18.0。

1台nginx容器。里面开4个端口:

5001-5003 当作节点。80 作为入口。

规划

端口 响应
5001 🟢 状态码 200 📄 纯文本 “5001”
5002 🟢 状态码 200 📄 纯文本 “5002”
5003 🟢 状态码 200 📄 纯文本 “5003”
80 根据负载规则转发流量

具体配置

端口 5001-5003 server 块

server {
  # 监听端口 5001
  listen *:5001;
  location / {
    # 增加头部 Content-Type
    add_header 'Content-Type' 'text/plain';
    # 返回 200,内容 5001
    return 200 '5001';
  }
}

# 类推,这是 5002 端口。
server {
  listen *:5002;
  location / {
    add_header 'Content-Type' 'text/plain';
    return 200 '5002';
  }
}

# 类推 5003 端口
# ...

端口 80 入口 server 块

# 定义服务器群,叫 cluster
# 后续主要修改 upstream 块
upstream cluster {
  server 127.0.0.1:5001;
  server 127.0.0.1:5002;
  server 127.0.0.1:5003;
}
# 入口服务器配置
server {
  listen *:80;
  server_name default_server;
  location / {
    # 代理到服务器群 cluster。
    proxy_pass http://cluster;
  }
}

ℹ️ 这些块可以写到同一个配置文件里面

负载均衡策略

原生 Nginx 自带下面3种负载均衡策略。

round-robin (循环)

在 5001-5003之间 循环转发。每个服务器都能有机会处理请求。

不指定算法则默认为 round-robin

upstream cluster {
  server 127.0.0.1:5001;
  server 127.0.0.1:5002;
  server 127.0.0.1:5003;
}

启动容器后,请求500次。

结果

Total: 500
5001 Hits: 166
5002 Hits: 167
5003 Hits: 167

least-connected (连接最少优先)

upstream cluster {
  least_conn;
  server 127.0.0.1:5001;
  server 127.0.0.1:5002;
  server 127.0.0.1:5003;
}

启动容器后,请求500次。

结果

Total: 500
5001 Hits: 167
5002 Hits: 167
5003 Hits: 166

和 Round-Robin 没有太大区别,但是在过程中可以看到 Hits 少的节点会被优先请求,直到它跟另2个节点响应了差不多数量的请求。

ip_hash (基于客户端ip)

集群里面的服务器按照ip组建hash环。入口按照客户端请求的IP做hash计算求得结果,然后找到离这个结果最近的hash的服务器。

upstream cluster {
  ip_hash;
  server 127.0.0.1:5001;
  server 127.0.0.1:5002;
  server 127.0.0.1:5003;
}

启动容器后,请求500次。

结果

Total: 500
5001 Hits: 0
5002 Hits: 500
5003 Hits: 0

因为每次以同样的IP发起请求,所以基于IP的hash计算结果不变,每次都转发到同样的节点。

weight (按权重配置)

可以手动配置每个节点的权重。Nginx优先转发到权重较大的节点。

upstream cluster {
  server 127.0.0.1:5001 weight=8;
  server 127.0.0.1:5002 weight=16;
  server 127.0.0.1:5003 weight=24;
}

启动容器后,请求500次。

结果

Total: 500
5001 Hits: 83
5002 Hits: 167
5003 Hits: 250

到这里看到,Nginx近似按照比例转发了。

节点健康检查

max_fails

nginx发现有一个节点响应超时的时候,这一次请求会转发至其他可用节点。然后下次会继续尝试,如果在一段时间内(默认10秒内)几次尝试都超时,则下一个10秒不会再转发到这个节点。

不写 max_fails 时,默认为 1.

如果 max_fails 为 0,则禁用这个节点的检查。

upstream cluster {
  server 127.0.0.1:5001;
  server 127.0.0.1:5002;
  server 127.0.0.1:5003 max_fails=3;
}

fail_timeout

可以指定nginx在尝试一个节点的时候,指定一段时间,如果这段时间内这个节点失败次数超过 max_fails 则后一段相同的时间都不再转发到这个节点,直到过完这个时间,重新尝试转发。

下面的配置描述了:当5003节点在5秒内失败了2次,则下一个5秒不再转发到5003,直到下一个5秒过完后,重新尝试5003。

upstream @serve_cluster {
  server 127.0.0.1:5001;
  server 127.0.0.1:5002;
  server 127.0.0.1:5003 max_fails=2 fail_timeout=5s;
}

附:检测负载均衡效果的方法

在宿主机做一个脚本,用curl给入口发请求。

如果得到5001表示来自5001端口,

如果得到5002表示来自5002端口,以此类推。

将收到的有效内容给对应端口的变量+1.

# 变量
HIT5001=0
HIT5002=0
HIT5003=0

# 按照 on_record <响应内容> 计数
function on_record {
  if [[ $1 == '5001' ]]; then
    # 获得 '5001',5001 计数
    HIT5001=$((HIT5001+1));
  elif [[ $1 == '5002' ]]; then
    # 获得 '5002',5002 计数
    HIT5002=$((HIT5002+1));
  elif [[ $1 == '5003' ]]; then
    # 获得 '5003',5003 计数
    HIT5003=$((HIT5003+1));
  else
    echo '这个内容不是5001-5003之间的值,它是:' $1
  fi
  clear
  echo 'Total(总请求数量):' $((HIT5001+HIT5002+HIT5003))
  echo '5001 Hits(响应次数):' $HIT5001
  echo '5002 Hits(响应次数):' $HIT5002
  echo '5003 Hits(响应次数):' $HIT5003
}

while true; do
  # 隐藏CURL请求过程的进度显示。让它只显示结果。
  output=$(curl http://localhost:80 2>/dev/null)
  # 拿到响应,让计数器处理。
  on_record $output
done;

参考

Nginx docker镜像

Using nginx as HTTP load balancer

Nginx的负载均衡max_fails和fail_timeout设置