Nginx的负载均衡

简单理解负载均衡

把前端超高并发访问转发到后端多台服务器进行处理,解决单个节点的压力过大;造成web服务器响应很慢,容易导致服务器瘫痪;所以把用户的需求交给后端的多台服务器帮忙响应;

负载均衡的工作原理

负载均衡有两种方式:四层负载均衡和七层负载均衡;

四层的负载均衡

工作在七层协议的第四层,主要是传输层,负责的是转发;

接收到客户端的流量后,修改数据包的信息地址,主要是目的地址和端口和源地址,将流量转发到应用服务器上;

七层的负载均衡

工作主要是在第七层,负责的工作是代理;

  • 首先:与客户建立一个完整的连接,并且分析客户的请求信息;
  • 然后按照调度算法选择应用服务器处理;
  • 最后,与应用服务器建立连接,让应用服务器处理真正的客户请求;

七层模型理解

  • 应用层:http、tftp、ftp、nfs、smtp
  • 表示层:Telnet、Rlogin、Snmp、Gopher
  • 会话层:SMTP/DNS
  • 传输层:TCP/UDP
  • 网络层:IP/ICMP/ARP/RARP/AKP/UUCP
  • 数据链路层:ppp
  • 物理层

七层负载均衡实验

规划:

前端服务器:192.168.75.130

后端服务器1:192.168.75.131

后端服务器2:192.168.75.132

注意:如果物理机不太行,可以使用多ip的虚拟主机来实现;

前端服务器的配置

  • upstream:主要是配置均衡池和调度方法;
  • proxy_pass:主要是配置代理服务器ip或者服务器组的名字
  • proxy_set_header:主要是配置转发给后端服务器的host和前端客户端真实ip;
实验:

配置前端服务器:130

#在http指令块下配置upstream,指定均衡组;
[root@node0 nginx]# vim conf/nginx.conf
http {
    include       mime.types;
    default_type  application/octet-stream;
        upstream web {				#这个就是配置web组
                server 192.168.75.131;
                server 192.168.75.132;
        }
	#在location指令块中配置proxy_pass

        location / {
                proxy_pass http://web;
                proxy_next_upstream error http_404 http_502;
                proxy_set_header Host $host;
                proxy_set_header X-Real-IP $remote_addr;
        }  
        
        
#配置的参数解释:   
        proxy_next_upstream:这里代表当后端服务器返回404或者是502的时候,把信息转发到其他的后端服务器,不会给到客户看到;
        proxy_set_header:代表把客户端的请求host转发给后端服务器
        proxy_set_header:把客户端的ip转发给后端服务器,$http_x_real_ip这个变量可以获取原始用户的ip;

准备后端服务器的页面

[root@node1 nginx]# curl 192.168.75.131
这是131 默认的nginx的主页面
[root@node2-132 ~]# curl 192.168.75.132
这是132的服务器

首先测试一下:

发现访问130,然后会交由后端服务器131和132处理请求;

#访问一下前端服务器130
	#这里很明显发现:访问130,会跳到后端131或者132服务器的页面;
[root@node2-132 ~]# curl 192.168.75.130
这是131 默认的nginx的主页面
[root@node2-132 ~]# curl 192.168.75.130
这是132的服务器
[root@node2-132 ~]# curl 192.168.75.130
这是131 默认的nginx的主页面
[root@node2-132 ~]# curl 192.168.75.130
这是132的服务器
均衡方式

设置轮询:

正常的,如果不设置权重,那么131会处理一次,132也会处理一次

就像正常访问一次;

决定权重的是在upstream 模块下的指定weight

#查看一下配置文件
[root@node0 ~]# vim /usr/local/nginx/conf/nginx.conf
......
http {
    include       mime.types;
    default_type  application/octet-stream;
        upstream web {
                server 192.168.75.131;	#这里没有设置权重;
                server 192.168.75.132;
        }
.........
        location / {
                proxy_pass http://web;
                proxy_next_upstream error http_404 http_502;
                proxy_set_header Host $host;
                proxy_set_header X-Real-IP $remote_addr;
        }
#直接设置权重
[root@node0 ~]# vim /usr/local/nginx/conf/nginx.conf
......
        upstream web {
                server 192.168.75.131 weight=3;
                server 192.168.75.132 weight=1;
        }
[root@node0 ~]# nginx -s reload

#访问130查看效果-->结合while循环进行验证
	#这里明显发现,131处理三次,然后132才处理一次;
[root@node0 ~]# while true;do curl 192.168.75.130;sleep 1; done
这是131 默认的nginx的主页面
这是131 默认的nginx的主页面
这是132的服务器
这是131 默认的nginx的主页面
这是131 默认的nginx的主页面
这是131 默认的nginx的主页面
这是132的服务器
这是131 默认的nginx的主页面
这是131 默认的nginx的主页面
这是131 默认的nginx的主页面
这是132的服务器
最大的连接次数:

如果说131或者132突然是崩溃了,那么会有什么情况?

#一直访问130,然后关闭131的nginx-->测试查看
[root@node0 ~]# while true;do curl 192.168.75.130;sleep 1; done
......
[root@node1 ~]# pkill nginx
	#这里我们能够发现131关闭后,处理是一直由132处理的;
	
#一直访问130,但是把131的主页给移除
[root@node0 ~]# while true;do curl 192.168.75.130;sleep 1; done
......
这是131 默认的nginx的主页面
<html>
<head><title>403 Forbidden</title></head>
#移除131的主页文件
[root@node1 ~]# mv /usr/local/nginx/html/index.html  /tmp

	#这里也能发现,131没有查找到资源的话会报403的错误

这里的原理是:错误的连接是由proxy_next_upstream和fastcgi_next_upstream等模块决定的,在默认的情况下,nginx会自动将请求交给正常的服务器处理,

首先关闭了proxy_next_upstream这个模块关闭,查看效果

[root@node0 ~]# vim /usr/local/nginx/conf/nginx.conf
..........
    include       mime.types;
    default_type  application/octet-stream;
        upstream web {
                server 192.168.75.131 weight=3 max_fails=3 fail_timeout=9s;
                server 192.168.75.132 weight=1;
        }
        location / {
                proxy_pass http://web;
                proxy_next_upstream off;
                #proxy_next_upstream error http_404 http_502;
                proxy_set_header Host $host;
                proxy_set_header X-Real-IP $remote_addr;
        }
#让131的nginx关闭
[root@node1 ~]# nginx -s stop

#重载配置文件,启动验证
[root@node0 ~]# while true;do curl 192.168.75.130;sleep 1; done
	#验证的过程发现,它除了交给132处理,还会有报错到屏幕
	.............
<body>
<h1>An error occurred.</h1>
<p>Sorry, the page you are looking for is currently unavailable.<br/>
Please try again later.</p>
<p>If you are the system administrator of this resource then you should check
the error log for details.</p>
<p><em>Faithfully yours, nginx.</em></p>
</body>
</html>
这是132的服务器

[root@node0 ~]# while true;do curl -I 192.168.75.130 2> /dev/null|grep HTTP/1.1 ;sleep 1; done 
HTTP/1.1 200 OK
HTTP/1.1 502 Bad Gateway
HTTP/1.1 200 OK
HTTP/1.1 200 OK
HTTP/1.1 200 OK

#这里需要注意,超时时间是9s-->由fail_timeout来决定
#最多尝试三次,这是遵循轮询的规则,不是一个请求,连接三次,而是轮询三次,有三次的处理机会;

现在在把proxy_next_upstream这个选项打开,重新测试

#修改配置文件
[root@node0 ~]# vim /usr/local/nginx/conf/nginx.conf
........
http {
    include       mime.types;
    default_type  application/octet-stream;
        upstream web {
                server 192.168.75.131 weight=3 max_fails=3 fail_timeout=9s;
                server 192.168.75.132 weight=1;
        }
......
        location / {
                proxy_pass http://web;
                #proxy_next_upstream off;
                proxy_next_upstream error http_404 http_502;
                proxy_set_header Host $host;
                proxy_set_header X-Real-IP $remote_addr;
        }
#重载配置文件查看结果
[root@node0 ~]# nginx -s reload
[root@node0 ~]# while true;do curl -I 192.168.75.130 2> /dev/null|grep HTTP/1.1 ;sleep 1; done
HTTP/1.1 200 OK
HTTP/1.1 200 OK
HTTP/1.1 200 OK
HTTP/1.1 200 OK

#原理:它的输出没有在屏幕上输出,是因为错误的响应码被proxy_next_upstream获取了,这次请求被转发到下一个正常的服务器期;
ip_hash

通过客户端ip进行hash,在通过hash值选择后端的服务器

#修改配置文件:
[root@node0 ~]# vim /usr/local/nginx/conf/nginx.conf
.......
http {
    include       mime.types;
    default_type  application/octet-stream;
        upstream web {
                ip_hash;			#这里主要添加一个ip_hash,启用ip_hash功能
                server 192.168.75.131 weight=3 max_fails=3 fail_timeout=9s;
                server 192.168.75.132 weight=1;
        }
............

        location / {
                proxy_pass http://web;
                #proxy_next_upstream off;
                proxy_next_upstream error http_404 http_502;
                proxy_set_header Host $host;
                proxy_set_header X-Real-IP $remote_addr;
        }
[root@node0 ~]# nginx -s reload

#客户端访问前端ip
[root@node0 ~]# curl 192.168.75.130
这是131 默认的nginx的主页面
[root@node0 ~]# curl 192.168.75.130
这是131 默认的nginx的主页面
[root@node0 ~]# curl 192.168.75.130
	#这里可以发现,132的nginx是正常的,但是访问的时候不会给到132处理
	
#我们把131关闭
[root@node1 ~]# nginx -s stop
	#请求就会转到132服务器上
[root@node0 ~]# curl 192.168.75.130
这是132的服务器
[root@node0 ~]# curl 192.168.75.130
这是132的服务器

使用负载均衡的时候,会遇到会话保持的问题,意思就是当你访问同样的资源的时候,原来访问过的服务器会帮你保持这个会话,不用从其他服务器来调取资源;

实现会话保持的方法:

  • ip_hash:根据哭护短的ip,将请求分配到不同的服务器上;
  • cookie:服务器给客户端下发一个cookie,具有特定的cookie的请求会分配给它的发布者;
url_hash

通过客户请求的url进行hash,在通过hash值来选择后端的server;

#修改前端nginx的配置文件
[root@node0 ~]# vim /usr/local/nginx/conf/nginx.conf
............
http {
    include       mime.types;
    default_type  application/octet-stream;
        upstream web {
                hash $request_uri consistent;
                server 192.168.75.131 ;
                server 192.168.75.132 ;
        }
[root@node0 ~]# nginx -s reload

#访问前端服务器130
[root@node0 ~]# curl 192.168.75.130
这是132的服务器
根据相应时间均衡

fail算法会根据后端节点的服务器的响应事假来分配请求,时间段的优先分配;

需要借助一个模块master.zip来进行实现;

#准备一下模块
[root@node0 src]# ls
nginx-1.17.10  nginx-1.17.10.tar.gz  nginx-upstream-fair-master.zip

#解压模块
[root@node0 src]# unzip nginx-upstream-fair-master.zip 
Archive:  nginx-upstream-fair-master.zip
a18b4099fbd458111983200e098b6f0c8efed4bc
   creating: nginx-upstream-fair-master/
  inflating: nginx-upstream-fair-master/.gdbinit  
  inflating: nginx-upstream-fair-master/README  
  inflating: nginx-upstream-fair-master/config  
  inflating: nginx-upstream-fair-master/ngx_http_upstream_fair_module.c  

#修改源码bug
[root@node0 src]# sed -i 's/default_port/no_port/g' nginx-upstream-fair-master/ngx_http_upstream_fair_module.c 

#重新编写预编译nginx
[root@node0 nginx-1.17.10]# ./configure --prefix=/usr/local/nginx --add-module=../nginx-upstream-fair-master

#编译安装
[root@node0 nginx-1.17.10]# make && make install

#编辑完了再次编辑130-nginx的配置文件
[root@node0 ~]# nginx -s reload
.....
http {
    include       mime.types;
    default_type  application/octet-stream;
        upstream web {
                fair;
                server 192.168.75.131 weight=1 max_fails=3 fail_timeout=9s;
                server 192.168.75.132 ;
        }
#重载配置文件
[root@node0 ~]# nginx -s reload
[root@node0 ~]# nginx -s reload

#访问前端ip-130
[root@node0 ~]# nginx -s reload
[root@node0 ~]# curl 192.168.75.130
这是132的服务器
[root@node0 ~]# cu
备用服务器配置

这里用132作为备用服务器,意思是只有主的服务器不能提供服务了,然后备用服务器才会顶上

#修改配置文件
[root@node0 ~]# !vim
vim /usr/local/nginx/conf/nginx.conf
..........
http {
    include       mime.types;
    default_type  application/octet-stream;
        upstream web {
                server 192.168.75.131 weight=1 max_fails=3 fail_timeout=9s;
                server 192.168.75.132 backup;
        }
#重载配置文件
[root@node0 ~]# nginx -s reload

#访问前端服务器
[root@node0 ~]#  curl 192.168.75.130
这是131 默认的nginx的主页面
[root@node0 ~]#  curl 192.168.75.130
这是131 默认的nginx的主页面

#把131的nginx关闭
[root@node0 ~]#  curl 192.168.75.130
这是132的服务器
[root@node0 ~]#

四层负载均衡配置实验

使用到四层均衡配置的话,主要是stream和upstream的配置,但是ngiinx默认编译的时候没有编译进去,所以需要重新编译;

配置四层负载均衡

130是前端服务器

#重新编译nginx然后添加stream模块
[root@node0 ~]# cd /usr/local/src/nginx-1.17.10/
[root@node0 nginx-1.17.10]# ./configure --prefix=/usr/local/nginx --with-stream
[root@node0 nginx-1.17.10]# make && make install


#部署四层均衡需要在全局的模块中设置
[root@node0 ~]# vim /usr/local/nginx/conf/nginx.conf
.......
events {
    worker_connections  1024;
}

stream {
        upstream web {
                server 192.168.75.131:80;		#注意必须制定port端口
                server 192.168.75.132:80;
        }
        server {								#这个server要卸载stream上
                listen 80;
                proxy_connect_timeout 3s;		#连接上游服务器超时间,超过则选择另外一个服务器
                proxy_timeout 10s;				#tcp连接闲置时间,超过则关闭
                proxy_pass web;					#均衡组
        }
}

#重载验证
[root@node0 ~]# nginx -s reload
[root@node0 ~]# curl 192.168.75.130
端口转发实验

修改配置文件

[root@node0 ~]# vim /usr/local/nginx/conf/nginx.conf
........
events {
    worker_connections  1024;
}
stream {
        upstream web {
                server 192.168.75.132:22;			#转发发哦22端口
        }
        server {
                listen 2222;
                proxy_connect_timeout 3s;
                proxy_timeout 10s;
                proxy_pass web;
        }
}
[root@node0 ~]# nginx -s reload

#验证-->神奇的发现,连接130会连接到132上面
[root@node3 ~]# ssh 192.168.75.130 -p 2222
The authenticity of host '[192.168.75.130]:2222 ([192.168.75.130]:2222)' can't be established.
ECDSA key fingerprint is SHA256:HopVZZv3jg6U4JiH16cbXzRJyxVhTpZ/R8DXKrtPDSI.
ECDSA key fingerprint is MD5:54:b3:96:c8:36:c4:74:8d:3f:f3:c2:fd:fb:46:56:7b.
Are you sure you want to continue connecting (yes/no)? yes
Warning: Permanently added '[192.168.75.130]:2222' (ECDSA) to the list of known hosts.
root@192.168.75.130's password: 
Last login: Sun Aug  1 07:15:05 2021 from 192.168.75.1
[root@node2-132 ~]#