# Nginx 代理 - 负载均衡实践
# 1.Nginx 负载均衡基本概述
# 1.1 什么是负载均衡
负载均衡 Load Balance,指的是将用户访问请求所产生的流量,进行平衡,分摊到多个应用节点处理。负载均衡扩展了应用的服务能力,增强了应用的可用性。
# 1.2 为什么需要负载均
当我们的 Web 服务器直接面向用户,往往要承载大量并发请求,单台服务器难以负荷,我使用多台 WEB 服务器组成集群,前端使用 Nginx 负载均衡,将请求分散的打到我们的后端服务器集群中,实现负载的流量分发。从而提升整体性能、以及系统的容灾能力。
# 1.3 负载均衡与代理区别
Nginx 负载均衡与 Nginx 反向代理不同地方在于:
- Nginx 代理仅代理一台服务器基于 URI 来调度,调度到不同功能的应用节点处理。
- Nginx 负载均衡则是将客户端请求通过 proxy_pass 代理至一组 upstream 资源池。
# 2.Nginx 负载均衡应用场景
# 2.1 四层负载均衡
四层负载均衡指的是 OSI 七层模型中的传输层,四层仅需要对客户端的请求进行 TCP/IP 协议的包代理就可以实
现负载均衡。IP+Port
四层负载均衡的性能极好、因为只需要底层进行转发处理,而不需要进行一些复杂的逻辑。
# 2.2 七层负载均衡
七层负载均衡工作在应用层,它可以完成很多应用方面的协议请求,比如我们说的 http 应用负载均衡,它可以实现 http 头信息的改写、安全应用规则控制、URI 匹配规则控制、及 rewrite 等功能,所以在应用层里面可以做的内容就更多了。
# 2.3 四层与七层区别
四层负载均衡:传输层
- 优点:性能高,数据包在底层就进行了转发
- 缺点:仅支持 ip:prot 转发,无法完成复杂的业务逻辑应用。
- MySQL TCP 3306, Redis、SSH、等;
七层负载均衡:应用层
- 优点:贴近业务,支持 URI 路径匹配、Header 改写、Rewrite 等
- 缺点:性能低,数据包需要拆解到顶层才进行调度,消耗随机端口;
# 3.Nginx 负载均衡配置场景
# 3.1 负载均衡场景环境规划
负载均衡场景架构图规划:
负载均衡场景地址规划:
角色 | 外网 IP (NAT) | 内网 IP (LAN) | 主机名 |
---|---|---|---|
lb01 | eth0:10.0.0.5 | eth1:172.16.1.5 | lb01 |
web01 | eth0:10.0.0.7 | eth1:172.16.1.7 | web01 |
web02 | eth0:10.0.0.8 | eth1:172.16.1.8 | web02 |
# 3.2 后端 Web 节点配置实例
1.Web01 服务器上配置为应用服务节点,创建对应 html 文件
[root@web01 ~]# cat /etc/nginx/conf.d/web.hmallleasing.com.conf | |
server { | |
listen 80; | |
server_name web.hmallleasing.com; | |
root /code/web; | |
location / { | |
index index.html; | |
} | |
} | |
[root@web01 conf.d]# mkdir /code/web | |
[root@web01 conf.d]# echo "Web01..." > /code/web/index.html | |
[root@web01 conf.d]# systemctl restart nginx |
2.Web02 服务器上配置为应用服务节点,创建对应 html 文件
[root@web02 ~]# cat /etc/nginx/conf.d/web.hmallleasing.com.conf | |
server { | |
listen 80; | |
server_name web.hmallleasing.com; | |
root /code/web; | |
location / { | |
index index.html; | |
} | |
} | |
[root@web02 conf.d]# mkdir /code/web | |
[root@web02 conf.d]# echo "Web02..." > /code/web/index.html | |
[root@web02 conf.d]# systemctl restart nginx |
# 3.3. 前端接入 Nginx 负载均衡
1. 将 lb01 配置为负载均衡,将所有请求代理至虚拟资源池
[root@lb01 ~]# cat /etc/nginx/conf.d/proxy_web.hmallleasing.com.conf | |
upstream web { | |
server 172.16.1.7:80; | |
server 172.16.1.8:80; | |
} | |
server { | |
listen 80; | |
server_name web.hmallleasing.com; | |
location / { | |
proxy_pass http://web; | |
include proxy_params; | |
} | |
} | |
[root@lb01 conf.d]# systemctl restart nginx |
2. 准备 Nginx 负载均衡需要使用的 proxy_params 文件
[root@lb01 conf.d]# vim /etc/nginx/proxy_params | |
proxy_http_version 1.1; | |
proxy_set_header Connectin ""; | |
proxy_set_header Host $http_host; | |
proxy_set_header X-Real-IP $remote_addr; | |
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; | |
proxy_connect_timeout 60; | |
proxy_send_timeout 60; | |
proxy_read_timeout 120; | |
proxy_buffering on; | |
proxy_buffer_size 32k; | |
proxy_buffers 4 128k; | |
proxy_temp_file_write_size 10240k; | |
proxy_max_temp_file_size 10240k; |
# 3.4. 浏览器访问测试负载效果
使用浏览器访问 web.oldxu.com,然后进行刷新测试。
# 4.Nginx 负载均衡调度算法
调度算法 | 概述 |
---|---|
轮询 | 按时间顺序逐一分配到不同的后端服务器 (默认) |
weight | 加权轮询,weight 值越大,分配到的访问几率越高 |
ip_hash | 每个请求按访问 IP 的 hash 结果分配,这样来自同一 IP 的固定访问一个后端服务器 |
url_hash | 根据请求 uri 进行调度到指定节点 |
least_conn | 将请求传递到活动连接数最少的服务器。 |
# 4.1 轮询调度算法
轮询调度算法的原理是将每一次用户的请求,轮流分配给内部中的服务器。
轮询算法的优点是其简洁性,它无需记录当前所有连接的状态,所以它是一种无状态调度。
[root@lb01 ~]# cat /etc/nginx/conf.d/proxy_web.hmallleasing.com.conf | |
upstream web { | |
server 192.168.40.7:80; | |
server 192.168.40.8:80; | |
} | |
server { | |
listen 80; | |
server_name web.hmallleasing.com; | |
location / { | |
proxy_pass http://web; | |
include proxy_params; | |
} | |
} |
# 4.2 加权轮询调度算法
轮询调度算法没有考虑每台服务器的处理能力,在实际情况中,由于每台服务器的配置、安装的业务应用等不同,其处理能力会不一样。所以,我们根据服务器的不同处理能力,给每个服务器分配不同的权值,使其能够接受相应权值数的服务请求。
[root@lb01 ~]# cat /etc/nginx/conf.d/proxy_web.hmallleasing.com.conf | |
upstream web { | |
server 192.168.40.7:80 weight=5; | |
server 192.168.40.8:80 weight=1; | |
} | |
server { | |
listen 80; | |
server_name web.hmallleasing.com; | |
location / { | |
proxy_pass http://web; | |
include proxy_params; | |
} | |
} |
# 4.3 ip_hash 调度算法
ip_hash 是基于用户请求的 IP,对该 IP 进行 hash 运算,根据 hash 运算的值,将请求分配到后端特定的一台节点进行处理。ip_hash 算法实现公式:hash (ip) % node_counts = index
如何配置 ip_hash 调度算法:
[root@lb01 ~]# cat /etc/nginx/conf.d/proxy_web.hmallleasing.com.conf | |
upstream web { | |
ip_hash; | |
server 192.168.40.7:80; | |
server 192.168.40.8:80; | |
} | |
server { | |
listen 80; | |
server_name web.hmallleasing.com; | |
location / { | |
proxy_pass http://web; | |
include proxy_params; | |
} | |
} |
ip_hash 调度算法会带来两个问题:
- 1. 如果有大量来自于同一 IP 的请求会造成某个后端节点流量过大,而其他节点无流量
- 2. 如果临时下线一台节点,会出现重新计算 hash 值,官方建议将下线节点标记为 down 状态,以保留客户端 IP 地址的当前哈希值。(如下图所示:)
如果有大量的用户调度到某一节点,而该节点刚好故障,则该算法会重新计算结果,从而造成大量的用户被转移其他节点处理,而需要重新建立会话。
# 4.4 一致性 hash 调度算
为了规避上述 hash 情况,一致性 hash 算法就诞生,一致性 Hash 算法也是使用取模的方法,但不是对服务器节点数量进行取模,而是对 2 的 32 方取模。即,一致性 Hash 算法将整个 Hash 空间组织成一个虚拟的圆环,Hash 函数值的空间为 0 ~ 2^32 - 1,整个哈希环如下:
https://www.jianshu.com/p/528ce5cd7e8f
[root@lb01 ~]# cat /etc/nginx/conf.d/proxy_web.hmallleasing.com.conf | |
upstream web { | |
hash $remote_addr consistent; | |
server 192.168.40.7:80; | |
server 192.168.40.8:80; | |
} | |
server { | |
listen 80; | |
server_name web.hmallleasing.com; | |
location / { | |
proxy_pass http://web; | |
include proxy_params; | |
} | |
} |
# 4.5 url_hash 调度算法
根据用户请求的 URL 进行 hash 取模,根据 hash 运算的值,将请求分配到后端特定的一台节点进行处理。URL 算法使用场景如下:client-->nginx-->url_hash-->cache1-->app
- 1. 用户请求 nginx 负载均衡器,通过 url 调度算法,将请求调度至 Cache1
- 2. 由于 Cache1 节点没有对应的缓存数据,则会请求后端获取,然后返回数据,并将数据缓存起来;
- 3. 当其他用户再次请求此前相同的 URL 时,此时调度器依然会调度至 Cache1 节点处理;
- 4. 由于 Cache1 节点已存在该 URL 资源缓存,所以直接将缓存数据进行返回;能大幅提升网站的响应;
1. 配置后端节点
# web1 节点 | |
[root@web01 ~]# echo "web1 Url1" > /code/web/url1.html | |
[root@web01 ~]# echo "web1 Url2" > /code/web/url2.html | |
# web2 节点 | |
[root@web02 ~]# echo "web2 Url1" > /code/web/url1.html | |
[root@web02 ~]# echo "web2 Url2" > /code/web/url2.html |
2. 负载均衡配置 url_hash 调度算法
[root@lb01 ~]# cat /etc/nginx/conf.d/proxy_web.hmallleasing.com.conf | |
upstream web { | |
# 请求同一个 url,会始终定向到同一个服务器节点,consistent 表示使用一致性 hash 算法 | |
hash $request_uri consistent; | |
server 192.168.40.7:80; | |
server 192.168.40.8:80; | |
} | |
server { | |
listen 80; | |
server_name web.hmallleasing.com; | |
location / { | |
proxy_pass http://web; | |
include proxy_params; | |
} | |
} |
3.Client 测试,会发现请求相同的 URL,始终会被定向至某特定后端节点。
[root@lb01 ~]# curl -HHost:web.hmallleasing.com http://192.168.40.150/url1.html | |
web2 Url1 | |
[root@lb01 ~]# curl -HHost:web.hmallleasing.com http://192.168.40.150/url2.html | |
web1 Url2 |
# 4.6 least_conn 调度算法
least_conn 调度算法实现原理,哪台节点连接数少,则将请求调度至哪台节点。
假设:A 节点有 1000 个连接 、b 节点有 500 连接,如果此时新的连接进入会分发给 b 节点
[root@lb01 ~]# cat /etc/nginx/conf.d/proxy_web.hmallleasing.com.conf | |
upstream web { | |
least_conn; | |
server 192.168.40.7:80; | |
server 192.168.40.8:80; | |
} | |
server { | |
listen 80; | |
server_name web.hmallleasing.com; | |
location / { | |
proxy_pass http://web; | |
include proxy_params; | |
} | |
} |
# 5.Nginx 负载均衡后端状态
后端 Web 节点在前端 Nginx 负载均衡调度中的状态
状态 | 概述 |
---|---|
down | 当前的 server 暂时不参与负载均衡 |
backup | 预留的备份服务器 |
max_fails | 允许请求失败的次数 |
fail_timeout | 经过 max_fails 失败后,服务暂停时间 |
max_conns | 限制最大的接收连接数 |
# 5.1 max_conns 限制连接数
max_conns 用来限制每个后端节点能够接收的最大 TCP 连接数,如果超过此连接则会抛出错误。
[root@lb01 ~]# cat /etc/nginx/conf.d/proxy_web.hmallleasing.com.conf | |
upstream web { | |
#接收的最大 TCP 连接数 | |
server 192.168.40.7:80 max_conns=2; | |
server 192.168.40.8:80 max_conns=2; | |
} | |
server { | |
listen 80; | |
server_name web.hmallleasing.com; | |
location / { | |
proxy_pass http://web; | |
include proxy_params; | |
} | |
} |
1、配置 jmeter
a、添加线程组:添加 -> 线程用户 -> 线程组
b、添加 HTTP 请求:添加 -> 取样器 ->HTTP 请求
c. 添加聚合报告:添加 -> 监听器 -> 聚合报告
d. 添加察看结果树:添加 -> 监听器 -> 察看结果树
e. 添加用表格察看结果:添加 -> 监听器 -> 用表格察看结果
2、通过 jmter 压力测试发现,当后端节点处理的连接数非常多的时候,当大于 4 个连接,其余的连接则会抛出异常,也就是每次仅能满足 4 个连接。
# 5.2 down 标识关闭状态
down 将服务器标记为不可用状态。
[root@lb01 ~]# cat /etc/nginx/conf.d/proxy_web.hmallleasing.com.conf | |
upstream web { | |
#临时停机维护 | |
server 192.168.40.7:80 down; | |
server 192.168.40.8:80; | |
} | |
server { | |
listen 80; | |
server_name web.hmallleasing.com; | |
location / { | |
proxy_pass http://web; | |
include proxy_params; | |
} | |
} |
# 5.3 backup 标识备份状态
backup 将服务器标记为备份服务器。当主服务器不可用时,将请求传递至备份服务器处理。
[root@lb01 ~]# cat /etc/nginx/conf.d/proxy_web.hmallleasing.com.conf | |
upstream web { | |
server 192.168.40.7:80 backup; | |
server 192.168.40.8:80; | |
} | |
server { | |
listen 80; | |
server_name web.hmallleasing.com; | |
location / { | |
proxy_pass http://web; | |
include proxy_params; | |
} | |
} |
# 5.4 max_fails 与 fail_timeout
max_fails=2 服务器通信失败尝试 2 次,任然失败,认为服务器不可用,默认值 1;
fail_timeout=5s 服务器通信失败后,每 5s 探测一次节点是否恢复可用,默认值 10s;
在 fail_timeout 设定的时间内,与服务器连接失败达到 max_fails 则认为服务器不可用;
[root@lb01 ~]# cat /etc/nginx/conf.d/proxy_web.hmallleasing.com.conf | |
upstream web { | |
#失败尝试 2 次,如果失败了每 5s 探测一次 | |
server 192.168.40.7:80 max_fails=2 fail_timeout=5s; | |
server 192.168.40.8:80 max_fails=2 fail_timeout=5s; | |
} | |
server { | |
listen 80; | |
server_name web.hmallleasing.com; | |
location / { | |
proxy_pass http://web; | |
include proxy_params; | |
} | |
} |
# 5.5 keepalived 提升吞吐量
keepalive 主要是与后端服务器激活缓存,也就是长连接,主要用来提升网站吞吐量。
默认情况下:没有与后端服务启用长连接功能,当有请求时,需要建立连接、维护连接、关闭连接,所以会存在网络消耗,但如果将所有的连接都缓存了,当连接空闲了又会占用其系统资源,所以可以使用 keepalive 参数来激活缓存,同时还可以使用 keepalive 参数来限制最大的空闲连接数;
[root@lb01 ~]# cat /etc/nginx/conf.d/proxy_web.hmallleasing.com.conf | |
upstream web { | |
#keepalive 32; #最大的空闲连接数 | |
#keepalive_timeout 100s; #空闲连接的超时时间 | |
server 192.168.40.7:80; | |
server 192.168.40.8:80; | |
} | |
server { | |
listen 80; | |
server_name web.hmallleasing.com; | |
location / { | |
proxy_pass http://web; | |
include proxy_params; | |
} | |
} | |
[root@lb01 ~]# cat /etc/nginx/proxy_params | |
#proxy_http_version 1.1; | |
#proxy_set_header Connectin ""; | |
proxy_set_header Host $http_host; | |
proxy_set_header X-Real-IP $remote_addr; | |
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; | |
proxy_connect_timeout 60; | |
proxy_send_timeout 60; | |
proxy_read_timeout 120; | |
proxy_buffering on; | |
proxy_buffer_size 32k; | |
proxy_buffers 4 128k; | |
proxy_temp_file_write_size 10240k; | |
proxy_max_temp_file_size 10240k; |
没有启用 keepalive 参数前,对集群进行压力测试
配置 Nginx 负载均衡启用 keepalive 参数:
[root@lb01 ~]# cat /etc/nginx/conf.d/proxy_web.hmallleasing.com.conf | |
upstream web { | |
keepalive 32; #最大的空闲连接数 | |
keepalive_timeout 100s; #空闲连接的超时时间 | |
server 192.168.40.7:80; | |
server 192.168.40.8:80; | |
} | |
server { | |
listen 80; | |
server_name web.hmallleasing.com; | |
location / { | |
proxy_pass http://web; | |
include proxy_params; | |
} | |
} | |
[root@lb01 ~]# cat /etc/nginx/proxy_params | |
#启用长连接 | |
proxy_http_version 1.1; | |
proxy_set_header Connectin ""; | |
proxy_set_header Host $http_host; | |
proxy_set_header X-Real-IP $remote_addr; | |
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; | |
proxy_connect_timeout 60; | |
proxy_send_timeout 60; | |
proxy_read_timeout 120; | |
proxy_buffering on; | |
proxy_buffer_size 32k; | |
proxy_buffers 4 128k; | |
proxy_temp_file_write_size 10240k; | |
proxy_max_temp_file_size 10240k; |
对启用 keepalive 参数后的集群进行压力测试,发现整体性能提升了一倍。
# 6. 后端节点异常容错机制
使用 nginx 负载均衡时,如何将后端请求超时的服务器流量平滑的切换到另一台上。
如果后台服务连接超时,Nginx 是本身是有机制的,如果出现一个节点 down 掉的时候,Nginx 会更据你具体负载均衡的设置,将请求转移到其他的节点上,但是,如果后台服务连接没有 down 掉,而是返回了错误异常码如:504、502、500,该怎么办
# 6.1 配置语法
# 当其中一台返回错误码 404,500 等错误时,可以分配到下一台服务器程序继续处理,提高平台访问成功率。 | |
proxy_next_upstream http_500 | http_502 | http_503 | http_504 |http_404; |
# 6.2 场景示例
[root@lb01 ~]# cat /etc/nginx/conf.d/proxy_web.hmallleasing.com.conf | |
upstream web { | |
keepalive 32; #最大的空闲连接数 | |
keepalive_timeout 100s; #空闲连接的超时时间 | |
server 192.168.40.7:80; | |
server 192.168.40.8:80; | |
} | |
server { | |
listen 80; | |
server_name web.hmallleasing.com; | |
location / { | |
proxy_pass http://web; | |
include proxy_params; | |
#当其中一台返回错误 500,502,503,504 时,分配下一台服务器程序处理,提高平台访问成功率 | |
proxy_next_upstream error timeout http_500 http_502 http_503 http_504; | |
proxy_next_upstream_tries 2; | |
proxy_next_upstream_timeout 3s; | |
} | |
} | |
[root@lb01 ~]# cat /etc/nginx/proxy_params | |
#启用长连接 | |
proxy_http_version 1.1; | |
proxy_set_header Connectin ""; | |
proxy_set_header Host $http_host; | |
proxy_set_header X-Real-IP $remote_addr; | |
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; | |
proxy_connect_timeout 60; | |
proxy_send_timeout 60; | |
proxy_read_timeout 120; | |
proxy_buffering on; | |
proxy_buffer_size 32k; | |
proxy_buffers 4 128k; | |
proxy_temp_file_write_size 10240k; | |
proxy_max_temp_file_size 10240k; |