为什么需要负载均衡
随着网站访问量的快速增长,单台服务器已经无法承担大量用户的并发访问,必须采用多台服务器协同工作,以提高计算机系统的处理能力和计算强度,满足当前业务量的需求。而如何在完成同样功能的多个网络设备之间实现合理的业务量分配,使之不会出现一台设备过忙、而其他的设备却没有充分使用的情况。要解决这一问题,可以采用负载均衡的方法。
负载均衡的原理
负载均衡是由多台服务器以对称的方式组成一个服务器集合,每台服务器都具有等价的地位,都可以单独对外提供服务而无须其他服务器的辅助。
通过某种负载分担技术,将外部发送来的请求均匀分配到对称结构中的某一台服务器上,而接收到请求的服务器独立地回应客户的请求。
负载均衡能够平均分配客户请求到服务器阵列,借此快速获取重要数据,解决大量并发访问服务问题。这种集群技术可以用最少的投资获得接近于大型主机的性能。
跨多个应用程序实例的负载均衡衡是一种常用技术,用于优化资源利用率,最大化吞吐量,减少延迟并确保容错配置。
如何配置
要开始使用NGINX 将HTTP通信负载平衡到一组服务器,首先需要使用upstream指令定义该组。该指令放在http上下文中。组中的服务器使用server指令进行配置。
例如,以下配置定义了一个名为backend的组,它由三个服务器配置组成(可以在三个以上的实际服务器中解析):
http {
upstream backend {
server backend1.example.com weight=5;
server backend2.example.com;
server 192.0.0.1 backup;
}
}
server指令语法
Syntax: server address [parameters];
Default: —
Context: upstream
定义服务器的地址和其他参数。地址可以指定为带可选端口的域名或IP地址,也可以指定在“ unix:”前缀之后指定的UNIX域套接字路径。如果未指定端口,则使用端口80。 解析为多个IP地址的域名一次定义了多个服务器。
可以定义以下参数:
1:weight=number
默认情况下,将服务器的权重设置为1。
2:max_conns=number
限制到代理服务器的同时活动连接的最大数量,默认值为0,表示没有限制。
如果服务器组不驻留在共享内存中,则限制对每个工作进程都有效。
如果启用了idle keepalive连接、多个workers和共享内存,到代理服务器的活动和空闲连接的总数可能超过max_conns值。
3:max_fails=number
设置应该在fail_timeout参数设置的持续时间内发生的与服务器通信失败尝试的次数,以考虑在fail_timeout参数设置的持续时间内服务器不可用。默认情况下,不成功的尝试数设置为1。零值将禁用尝试计数。
认为失败的尝试是由proxy_next_upstream,fastcgi_next_upstream,uwsgi_next_upstream,scgi_next_upstream,memcached_next_upstream和grpc_next_upstream指令定义的。
4:fail_timeout=time
在与服务器通信失败的次数达到指定次数时,服务器不可用;
这段时间服务器将被认为是不可用的。默认情况下,参数设置为10秒。
5:backup
将服务器标记为备份服务器。它将在主服务器不可用时传递请求。 该参数不能与hash、ip_hash和random负载均衡方法一起使用。
6:down
将服务器标记为永久不可用。
指定服务器组
要将请求传递到服务器组,请在proxy_pass指令中指定组的名称(或者这些协议的fastcgi_pass、memcached_pass、scgi_pass或uwsgi_pass指令)。
在下一个示例中,在NGINX上运行的虚拟服务器将所有请求传递给上一个示例中定义的backend upstream组:
server {
location / {
proxy_pass http://backend;
}
}
负载均衡算法
开源NGINX支持三种负载均衡方法
1: Round Robin-请求在服务器之间均匀分布,并考虑server weights。默认情况下使用此方法
2: Least Connections -将请求发送到具有最少活动连接数的服务器,同时考虑server weights
3: IP Hash 从客户端IP地址确定向其发送请求的服务器。在这种情况下,使用IPv4地址的前三个八位字节或整个IPv6地址来计算哈希值。该方法保证来自同一地址的请求到达同一服务器,除非它不可用。
如果需要从负载平衡轮询中临时删除其中一个服务器,则可以使用down参数对其进行标记,以便保留客户端IP地址的当前哈希值。
服务器权重
默认情况下,NGINX使用Round Robin方法根据权重在组中的服务器之间分配请求。
server指令的weight参数设置服务器的权重; 默认值为1:
upstream backend {
server backend1.example.com weight=5;
server backend2.example.com;
server 192.0.0.1 backup;
}
在示例中,backend1.example.com的权重为5; 其他两台服务器具有默认权重(1),但IP地址为192.0.0.1的服务器被标记为备份服务器,除非其他两台服务器都不可用,否则不会接收请求。
通过这种权重配置,每六个请求中就有五个发送到backend1.example.com,一个发送到backend2.example.com。
健康检查
当NGINX认为服务器不可用时,它会暂时停止向服务器发送请求,直到它再次被视为活动状态。
server指令的以下参数配置NGINX认为服务器不可用的条件:
1:max_fails 设置连续失败尝试的次数,之后NGINX将服务器标记为不可用
2:fail_timeout设置max_fails参数指定的失败尝试次数必须发生的时间,以使服务器被视为不可用,以及设置NGINX标记服务器后认为服务器不可用的时间长度。
默认值为1次尝试和10秒。因此,如果服务器不接受或不响应(即一个)请求,NGINX立即认为服务器在10秒内不可用。
以下示例显示如何设置这些参数:
upstream backend {
server backend1.example.com;
server backend2.example.com max_fails=3 fail_timeout=30s;
server backend3.example.com max_fails=2;
}
下面开始案例。
#我们先创建四个目录
$ ~/D/www> mkdir {front,back1,back2,back3}
然后配置四个虚拟域名
server {
listen 80;
server_name test.front.com;
index index.html index.htm index.php;
root /www/front;
location / {
try_files $uri @rewriteapp;
}
location @rewriteapp {
rewrite ^(.*)$ /index.php/$1 last;
}
access_log /var/log/front_access.log;
error_log /var/log/front_error.log;
location ~* \.(?:jpg|jpeg|gif|png|ico|cur|gz|svg|svgz|mp4|ogg|ogv|webm|htc|mp3|swf|xlsx)$ {
expires 1M;
access_log off;
add_header Cache-Control “public”;
}
location ~* \.(?:css|js|swf|xlsx)$ {
expires 1y;
access_log off;
add_header Cache-Control “public”;
}
location ~ ^/.*\.php(/|$) {
fastcgi_pass 127.0.0.1:9000;
fastcgi_split_path_info ^(.+\.php)(/.*)$;
fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name;
include fastcgi_params;
}
}
然后将配置文件复制一下
$ /u/l/e/n/sites-enabled> cp front back1
$ /u/l/e/n/sites-enabled> cp front back2
$ /u/l/e/n/sites-enabled> cp front back3
然后将每个配置文件的server_name,root,access_log和error_log,以及端口也改一下。
test.back1.com监听8081端口,test.back2.com监听8082端口,test.back3.com监听8083端口
然后再到电脑的/etc/host里面加上这四个域名。
127.0.0.1 test.front.com
127.0.0.1 test.back1.com
127.0.0.1 test.back2.com
127.0.0.1 test.back3.com
接着重启一下nginx
$ /u/l/e/n/sites-enabled> sudo nginx -s reload
Password:
然后在front目录下面创建两个文件,分别为index.html和index.php
$ ~/D/w/front> cat index.html
file=index.html; host_name=test.front.com
$ ~/D/w/front> cat index.php
<?php echo "file=index.php; host_name=test.front.com";
然后我们将文件复制到back1,back2,back2目录
$ ~/D/w/front> cp index.html ../back1/
$ ~/D/w/front> cp index.html ../back2/
$ ~/D/w/front> cp index.html ../back3/
$ ~/D/w/front> cp index.php ../back1/
$ ~/D/w/front> cp index.php ../back2/
$ ~/D/w/front> cp index.php ../back3/
然后将各自的index.html和index.php中的host_name替换一下。
这时候我们访问一下这几个域名
$ ~/D/w/front> curl http://test.front.com
file=index.php;host_name=test.front.com
$ ~/D/w/front> curl http://test.back1.com
file=index.php;host_name=test.back1.com
$ ~/D/w/front> curl http://test.back2.com
file=index.php;host_name=test.back2.com
$ ~/D/w/front> curl http://test.back3.com
file=index.php;host_name=test.back3.com
我们修改一下front的nginx配置
$ /u/l/e/n/sites-enabled> cat front
upstream backend {
server test.back1.com:8081;
server test.back2.com:8082;
server test.back3.com:8083;
}
server {
listen 80;
server_name test.front.com;
access_log /var/log/front_access.log;
error_log /var/log/front_error.log;
location / {
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-Server $host;
proxy_pass http://backend;
}
}
然后我们重启一下nginx
$ /u/l/e/n/sites-enabled> sudo nginx -s reload
然后我们访问一下test.front.com
$ /u/l/e/n/sites-enabled> curl http://test.front.com
file=index.php; host_name=test.back1.com
$ /u/l/e/n/sites-enabled> curl http://test.front.com
file=index.php; host_name=test.back2.com
$ /u/l/e/n/sites-enabled> curl http://test.front.com
file=index.php; host_name=test.back3.com
我们也可以看到了,请求都到test.back对应的域名去了。
我们可以改下负载均衡的算法,按照上面说的: IP Hash ,该方法保证来自同一地址的请求到达同一服务器。那么我就修改一下配置
upstream backend {
ip_hash;
server test.back1.com:8081;
server test.back2.com:8082;
server test.back3.com:8083;
}
server {
listen 80;
server_name test.front.com;
access_log /var/log/front_access.log;
error_log /var/log/front_error.log;
location / {
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-Server $host;
proxy_pass http://backend;
}
}
修改nginx配置一定要记得重启nginx。
$ /u/l/e/n/sites-enabled> curl http://test.front.com
file=index.php; host_name=test.back3.com
$ /u/l/e/n/sites-enabled> curl http://test.front.com
file=index.php; host_name=test.back3.com
$ /u/l/e/n/sites-enabled> curl http://test.front.com
file=index.php; host_name=test.back3.com
$ /u/l/e/n/sites-enabled> curl http://test.front.com
file=index.php; host_name=test.back3.com
$ /u/l/e/n/sites-enabled> curl http://test.front.com
file=index.php; host_name=test.back3.com
我们可以发现,所有的请求都转发到test.back3.com对应服务器上面去了。至于 Least Connections,因为我们是在本地测试,就先不测试了
我们还可以修改一下权重
upstream backend {
server test.back1.com:8081 weight=3;
server test.back2.com:8082 weight=2;
server test.back3.com:8083;
}
server {
listen 80;
server_name test.front.com;
access_log /var/log/front_access.log;
error_log /var/log/front_error.log;
location / {
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-Server $host;
proxy_pass http://backend;
}
}
我们设置了两个权重,一个是weight=3,一个是weight=2,还一个没有默认,那么就默认是1,
那么上面的配置的意思是如果有6个请求的话,有3个是到back,2个到back2,1个是back1。
$ /u/l/e/n/sites-enabled> curl http://test.front.com
file=index.php; host_name=test.back1.com
$ /u/l/e/n/sites-enabled> curl http://test.front.com
file=index.php; host_name=test.back2.com
$ /u/l/e/n/sites-enabled> curl http://test.front.com
file=index.php; host_name=test.back1.com
$ /u/l/e/n/sites-enabled> curl http://test.front.com
file=index.php; host_name=test.back3.com
$ /u/l/e/n/sites-enabled> curl http://test.front.com
file=index.php; host_name=test.back2.com
$ /u/l/e/n/sites-enabled> curl http://test.front.com
file=index.php; host_name=test.back1.com
我们也看到确实就是这样。
如果需要从负载均衡轮询中临时删除其中一个服务器,则可以使用down参数对其进行标记,我们也可以看一下
upstream backend {
server test.back1.com:8081 weight=3 down;
server test.back2.com:8082 weight=2;
server test.back3.com:8083;
}
server {
listen 80;
server_name test.front.com;
access_log /var/log/front_access.log;
error_log /var/log/front_error.log;
location / {
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-Server $host;
proxy_pass http://backend;
}
}
说明back1目前不可用了。
$ /u/l/e/n/sites-enabled> curl http://test.front.com
file=index.php;host_name=test.back2.com
$ /u/l/e/n/sites-enabled> curl http://test.front.com
file=index.php; host_name=test.back3.com
$ /u/l/e/n/sites-enabled> curl http://test.front.com
file=index.php;host_name=test.back2.com
$ /u/l/e/n/sites-enabled> curl http://test.front.com
file=index.php; host_name=test.back2.com
$ /u/l/e/n/sites-enabled> curl http://test.front.com
file=index.php; host_name=test.back3.com
$ /u/l/e/n/sites-enabled> curl http://test.front.com
file=index.php;host_name=test.back2.com
$ /u/l/e/n/sites-enabled> curl http://test.front.com
file=index.php;host_name=test.back2.com
$ /u/l/e/n/sites-enabled> curl http://test.front.com
file=index.php;host_name=test.back3.com
$ /u/l/e/n/sites-enabled> curl http://test.front.com
file=index.php; host_name=test.back2.com
参考资料:
《实战nginx:取代Apache的高性能Web服务器》
https://docs.nginx.com/nginx/admin-guide/load-balancer/http-load-balancer/#overview