Nginx负载均衡

  |  

Nginx负载均衡配置

概述

​ 负载均衡在服务端开发中算是一个比较重要的特性。由于Nginx除了作为常规的Web服务器外,还会被大规模的用于反向代理商前台,由于Nginx的异步框架可以解决很大的并发请求,把这些并发请求hold住之后即可以分发给后端服务端(backend servers, 后面简称backend)来做复杂的计算、解决和响应,并且在业务量添加的时候可以方便地扩容后端服务器。

​ 负载均衡可以分为硬件负载均衡和软件负载均衡,前者一般是专用的软件和硬件相结合的设施,设施商会提供完整成熟的处理方案,通常也会更加昂贵。软件的复杂均衡以Nginx占据绝大多数。

基本简介

负载均衡涉及到以下的基础知识。

什么是负载均衡

负载均衡可以将请求前端的请求分担到后端多个节点上,提升系统的响应和处理能力。

​ 负载均衡 建立在现有网络结构之上,它提供了一种廉价有效透明的方法扩展网络设备和服务器的带宽、增加吞吐量、加强网络数据处理能力、提高网络的灵活性和可用性。

​ 负载均衡,英文名称为Load Balance,其意思就是分摊到多个操作单元上进行执行,例如Web服务器、FTP服务器、企业关键应用服务器和其它关键任务服务器等,从而共同完成工作任务。

​ 简单的来说。负载均衡可以减少服务器的压力,将原本一台服务器所要承受的访问量分给了多台,并提高了项目的可用性,当一台服务器挂掉的时候也不会导致整个项目瘫痪。

​ 负载均衡是扩展应用程序并提高其性能和冗余的绝佳方法。Nginx是一种流行的Web服务器软件,可以配置为简单但功能强大的负载均衡器,以提高服务器资源的可用性和效率。在负载平衡配置中,nginx充当在多个单独服务器上工作的分布式Web应用程序的单个入口点。

负载均衡的目的

​ 负载均衡的目的是为了解决单个节点压力过大,造成Web服务响应过慢,严重的情况下导致服务瘫痪,无法正常提供服务,由于一个Web服务同时能处理的用户并发请求的数量有限,同时还有机器故障的情况,所以一个Web站点通常会在N台机器上各部署一套同样的程序。当某一个服务挂掉的时候,还有第二个、第三个、第N个服务。。。继续为用户提供服务,给用户的感觉,你的服务还在正常的运行!在这些提供同样服务的机器当中,在硬件配置方面也各不一样,这样就会存在部份机器性能非常好,能快速计算并响应用户的请求,另外一部份机器可能配置差点,响应用户的请求的时间会长一些。

​ 这就需要我们思考一个问题?如果有一个服务正在同时处理1000个用户的请求,这个服务的上限可能最多能同时处理1000个用户的请求,这时它已经很忙了,如果此时又有一个新请求过来,我们仍然把这个请求分配给这台机器,这时候这个请求就只能在干等着,等这个服务处理完那些请求后,再继续处理它。这样在浏览器中的反应就像12306我们在春节买票一样,卡在那不动了,让用户眼巴巴的干着急。而能提供同样服务的其它机器,这时确很空闲。这样不仅是对服务器资源的浪费,也充分发挥不出弄多台服务器装同一个服务的最高价值。我们通常称对某一台机器的访问量称为负载量,如何将一个用户的请求,合理的分配到一台能快速响应用户请求的服务器上,我们就需要用到一些负载策略。也就体现出了文章主题的用意了:负载均衡,将用户的所有HTTP请求均衡的分配到每一台机器上,充分发挥所有机器的性能,提高服务的质量和用户体验。负载均衡可以通过负载均衡网络硬件设备和Web服务器软件来实现,前者设备成本较高,小公司通常负担不起,所以后者一般是我们的首选。实现负载均衡常用的Web服务器软件有Nginx、HAProxy、LVS等,本文主要介绍Nginx的负载均衡策略。

负载均衡的常用功能

客户端的请求转发功能

​ 按照一定的算法【权重、轮询】,将客户端请求转发到不同应用服务器上,减轻单个服务器压力,提高系统并发量。

服务器的故障转移功能

​ 通过心跳检测的方式,判断应用服务器当前是否可以正常工作,如果服务器宕掉,自动将请求发送到其他应用服务器。

服务器故障恢复自动添加功能

​ 如检测到发生故障的应用服务器恢复工作,自动将其添加到处理用户请求队伍中。

负载均衡算法

Round Robin

​ 对所有的backend轮训发送请求,算是最简单的方式了,也是默认的分配方式

Least Connections(least_conn)

​ 跟踪和backend当前的活跃连接数目,最少的连接数目说明这个backend负载最轻,将请求分配给他,这种方式会考虑到配置中给每个upstream分配的weight权重信息;

Least Time(least_time)

​ 请求会分配给响应最快和活跃连接数最少的backend

IP Hash(ip_hash)

​ 对请求来源IP地址计算hash值,IPv4会考虑前3个octet,IPv6会考虑所有的地址位,而后根据得到的hash值通过某种映射分配到backend;

Generic Hash(hash)
以客户自己设置资源(比方URL)的方式计算hash值完成分配,其可选consistent关键字支持一致性hash特性;

会话一致性

​ 客户(浏览器)在和服务端交互的时候,通常会在本地保存少量信息,而整个过程叫做一个会话(Session)并用唯一的Session ID进行标识。会话的概念不仅用于购物车这种常见情况,由于HTTP协议是无状态的,所以任何需要逻辑上下文的情形都必需使用会话机制,此外HTTP用户端也会额外缓存少量数据在本地,这样即可以减少请求提高性能了。假如负载均衡可能将这个会话的请求分配到不同的后端服务端上,这一定是不合适的,必需通过多个backend共享这些数据,效率一定会很低下,最简单的情况是保证会话一致性——相同的会话每次请求都会被分配到同一个backend上去。

后端服务端的动态配置

​ 出问题的backend要能被及时探测并剔除出分配群,而当业务增长的时候可以灵活的增加backend数目。此外当前风靡的Elastic Compute云计算服务,服务商也应当根据当前负载自动增加和减少backend主机。

基于DNS的负载均衡

​ 通常现代的网络服务者一个域名会关连到多个主机,在进行DNS查询的时候,默认情况下DNS服务器会以round-robin形式以不同的顺序返回IP地址列表,因而天然将用户请求分配到不同的主机上去。不过这种方式含有固有的缺陷:DNS不会检查主机和IP地址的可访问性,所以分配给用户端的IP不确保是可用的(Google 404);DNS的解析结果会在用户端、多个中间DNS服务器不断的缓存,所以backend的分配不会那么的理想。

Nginx负载均衡

负载均衡用于从“upstream”模块定义的后端服务器列表中选取一台服务器接受用户的请求。一个最基本的upstream模块是这样的,模块内的server是服务器列表:

1
2
3
4
5
6
7
#动态服务器组
upstream dynamic_zuoyu {
server localhost:8080; #tomcat 7.0
server localhost:8081; #tomcat 8.0
server localhost:8082; #tomcat 8.5
server localhost:8083; #tomcat 9.0
}

在upstream模块配置完成后,要让指定的访问反向代理到服务器列表:

1
2
3
4
5
#其他页面反向代理到tomcat容器
location ~ .*$ {
index index.jsp index.html;
proxy_pass http://dynamic_zuoyu;
}

这就是最基本的负载均衡实例,但这不足以满足实际需求;目前Nginx服务器的upstream模块支持6种方式的分配

常用参数

参数描述
service反向服务地址 加端口
weight权重
fail_timeout与max_fails结合使用。
max_fails设置在fail_timeout参数设置的时间内最大失败次数,如果在这个时间内,所有针对该服务器的请求都失败了,那么认为该服务器会被认为是停机了,
max_conns允许最大连接数
fail_time服务器会被认为停机的时间长度,默认为10s。
backup标记该服务器为备用服务器。当主服务器停止时,请求会被发送到它这里。
down标记服务器永久停机了。
slow_start当节点恢复,不立即加入

负载均衡策略

在这里,只详细说明Nginx自带的负载均衡策略,第三方不多描述。

轮询默认方式
weight权重方式
ip_hash依据ip分配方式
least_conn最少连接方式
fair(第三方)响应时间方式
url_hash(第三方)依据URL分配方式
轮询

最基本的配置方法,上面的例子就是轮询的方式,它是upstream模块默认的负载均衡默认策略。每个请求会按时间顺序逐一分配到不同的后端服务器。

1
2
3
4
5
6
7
#动态服务器组
upstream dynamic_zuoyu {
server localhost:8080; #tomcat 7.0
server localhost:8081; #tomcat 8.0
server localhost:8082; #tomcat 8.5
server localhost:8083; #tomcat 9.0
}
注意
  • 在轮询中,如果服务器down掉了,会自动剔除该服务器。
  • 缺省配置就是轮询策略。
  • 此策略适合服务器配置相当,无状态且短平快的服务使用。
weight

权重方式,在轮询策略的基础上指定轮询的几率

1
2
3
4
5
6
7
#动态服务器组
upstream dynamic_zuoyu {
server localhost:8080 weight=2; #tomcat 7.0
server localhost:8081; #tomcat 8.0
server localhost:8082 backup; #tomcat 8.5
server localhost:8083 max_fails=3 fail_timeout=20s; #tomcat 9.0
}

​ weight参数用于指定轮询几率,weight的默认值为1,;weight的数值与访问比率成正比,比如Tomcat 7.0被访问的几率为其他服务器的两倍。

注意
  • 权重越高分配到需要处理的请求越多。
  • 此策略可以与least_conn和ip_hash结合使用。
  • 此策略比较适合服务器的硬件配置差别比较大的情况。
ip_hash

指定负载均衡器按照基于客户端IP的分配方式,这个方法确保了相同的客户端的请求一直发送到相同的服务器,以保证session会话。这样每个访客都固定访问一个后端服务器,可以解决session不能跨服务器的问题。

1
2
3
4
5
6
7
8
#动态服务器组
upstream dynamic_zuoyu {
ip_hash; #保证每个访客固定访问一个后端服务器
server localhost:8080 weight=2; #tomcat 7.0
server localhost:8081; #tomcat 8.0
server localhost:8082; #tomcat 8.5
server localhost:8083 max_fails=3 fail_timeout=20s; #tomcat 9.0
}
注意
  • 在nginx版本1.3.1之前,不能在ip_hash中使用权重(weight)。
  • ip_hash不能与backup同时使用。
  • 此策略适合有状态服务,比如session。
  • 当有服务器需要剔除,必须手动down掉。
least_conn

把请求转发给连接数较少的后端服务器。轮询算法是把请求平均的转发给各个后端,使它们的负载大致相同;但是,有些请求占用的时间很长,会导致其所在的后端负载较高。这种情况下,least_conn这种方式就可以达到更好的负载均衡效果。

1
2
3
4
5
6
7
8
#动态服务器组
upstream dynamic_zuoyu {
least_conn; #把请求转发给连接数较少的后端服务器
server localhost:8080 weight=2; #tomcat 7.0
server localhost:8081; #tomcat 8.0
server localhost:8082 backup; #tomcat 8.5
server localhost:8083 max_fails=3 fail_timeout=20s; #tomcat 9.0
}
注意
  • 此负载均衡策略适合请求处理时间长短不一造成服务器过载的情况。

第三方策略

第三方的负载均衡策略的实现需要安装第三方插件。

fair

按照服务器端的响应时间来分配请求,响应时间短的优先分配。

1
2
3
4
5
6
7
8
#动态服务器组
upstream dynamic_zuoyu {
server localhost:8080; #tomcat 7.0
server localhost:8081; #tomcat 8.0
server localhost:8082; #tomcat 8.5
server localhost:8083; #tomcat 9.0
fair; #实现响应时间短的优先分配
}
url_hash

按访问url的hash结果来分配请求,使每个url定向到同一个后端服务器,要配合缓存命中来使用。同一个资源多次请求,可能会到达不同的服务器上,导致不必要的多次下载,缓存命中率不高,以及一些资源时间的浪费。而使用url_hash,可以使得同一个url(也就是同一个资源请求)会到达同一台服务器,一旦缓存住了资源,再此收到请求,就可以从缓存中读取。

1
2
3
4
5
6
7
8
#动态服务器组
upstream dynamic_zuoyu {
hash $request_uri; #实现每个url定向到同一个后端服务器
server localhost:8080; #tomcat 7.0
server localhost:8081; #tomcat 8.0
server localhost:8082; #tomcat 8.5
server localhost:8083; #tomcat 9.0
}
Sticky

该策略在多台服务器的环境下,为了确保一个客户端只和一台服务器通讯,它会保持长连接,并在结束会话后再次选择一个服务器,保证了压力均衡。

1
2
3
4
5
6
7
upstream dynamic_zuoyu{
sticky;
server localhost:8080; #tomcat 7.0
server localhost:8081; #tomcat 8.0
server localhost:8082; #tomcat 8.5
server localhost:8083; #tomcat 9.0
}

​ 注意:如果浏览器不支持cookie,那么sticky不生效,毕竟整个模块是给予cookie实现。Sticky模块和ip_hash模块不能够同时使用。

常见问题

max_fails&fail_timeout无效

通过配置max_fails、fail_timeout来达到当一台服务器访问出现非200时可以跳转到另一台服务器
操作: 配置nginx.conf文件 具体配置如下

1
2
3
4
5
6
upstream report{   
server localhost1:18080 max_fails=10 fail_timeout=60s;
server localhost1:28080 max_fails=10 fail_timeout=60s;
server localhost2:18080 max_fails=10 fail_timeout=60s;
server localhost2:28080 max_fails=10 fail_timeout=60s;
}

现象:并没有什么卵用,如果一台服务器挂掉,nginx依然会分发到那台。

原因

max_fails=number设定Nginx与服务器通信的尝试失败的次数。在fail_timeout参数定义的时间段内,如果失败的次数达到此值,Nginx就认为服务器不可用。在下一个fail_timeout时间段,服务器不会再被尝试。 失败的尝试次数默认是1。设为0就会停止统计尝试次数,认为服务器是一直可用的。你可以通过指令proxy_next_upstreamfastcgi_next_upstreammemcached_next_upstream来配置什么是失败的尝试。 默认配置时,http_404状态不被认为是失败的尝试

而我们恰恰就是访问404就给分发到那台倒霉的服务器了

解决
1
2
3
4
5
6
7
8
9
location / {
proxy_pass http://tomcatserver1;
index index.html index.htm;
# proxy_next_upstream
proxy_next_upstream error timeout invalid_header http_500 http_503 http_404;
proxy_connect_timeout 20s;
proxy_read_timeout 20s;
proxy_send_timeout 20s;
}
proxy_next_upstream参数

proxy_next_upstream 相关参数描述

参数描述
error和后端服务器建立连接时,或者向后端服务器发送请求时,或者从后端服务器接收响应头时,出现错误
timeout和后端服务器建立连接时,或者向后端服务器发送请求时,或者从后端服务器接收响应头时,出现超时
invalid_header后端服务器返回空响应或者非法响应头
http_500后端服务器返回的响应状态码为500
http_502后端服务器返回的响应状态码为502
http_503后端服务器返回的响应状态码为503
http_504后端服务器返回的响应状态码为504
http_404后端服务器返回的响应状态码为404
off停止将请求发送给下一台后端服务器

总结

​ 总结一点,负载均衡不论是各种软件或硬件上的解决方案,主要还是将大量的并发请求按照一定的规律分发给不同的服务器处理,从而减少某台服务器的瞬时压力,提高网站的抗并发能力。nginx在负载均衡的应用之所以广泛,这归功于它的灵活配置,一个nginx.conf文件解决大部分问题,不论是nignx创建虚拟服务器、nginx的反向代理服务器,还是本文介绍的nginx的负载均衡,几乎都在这个配置文件中进行。服务器上只负责把nginx搭好,跑起来即可。而且它本身轻量级,不需要占用服务器太多资源就可以达到较好的效果。

 评论