负载均衡是由多台服务器以对称方式组成服务器集群,每台服务器都具等价关系,均可单独对外提供服务且无需其他服务器辅助。通过对应负载分担均衡,将外部发送请求按既定规则分发到对称集群架构其中一台服务器上,对应接收请求服务器也独立地回应客户端请求。均衡负载能够平均分配客户请求到服务器阵列,以快速获取数据,解决大并发访问服务的洪峰问题。此种集群技术可用最少的投入获得超高的性能,是一种性价比较高的并发解决方案之一。

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

负载均衡(LoadBalance)其原意就是前端请求分摊到多个操作单元进行取数执行操作,例如Web服务器、FTP服务器、企业关键应用服务器和其它关键任务服务器等,从而共同完成工作任务。


常用负载均衡方法 一、用户手动选择

nginx负载不平均 nginx负载均衡502_nginx负载不平均

这是一种比较古老的方式,目主要通过提供不同的线路、不同服务器链接的方式,来实现负载均衡。

这种方式在一些提供下载、在线影院、直播等网站比较常见。


优点:

  • 1、部署简单,新增服务器或链路只需增加对应的入口即可
  • 2、维护方便,如果有服务器宕机只需关闭对应入口即可


缺点:

  • 1、智能化程度不高,需用户手动切换
  • 2、分配不可控,容易出现服务器访问量极不平衡等情况


二、DNS轮询

nginx负载不平均 nginx负载均衡502_nginx负载不平均_02

nginx负载不平均 nginx负载均衡502_nginx负载不平均_03

大多域名注册商都支持多条A记录的解析,这就是DNS轮询,DNS服务器将解析请求按照A记录的顺序,逐一分配到不同的IP上,这样就完成了简单的负载均衡。


优点:

  • 1、基本上无成本,因为往往域名注册商的这种解析都是免费的;
  • 2、部署方便,除了网络拓扑的简单扩增,新增的Web服务器只要增加一个公网IP即可。


缺点:

  • 1、健康检查,如果某台服务器宕机,DNS服务器是无法知晓的,仍旧会将访问分配到此服务器。修改DNS记录全部生效起码要3-4小时,甚至更久;
  • 2、分配不均,如果几台Web服务器之间的配置不同,能够承受的压力也就不同,但是DNS解析分配的访问却是均匀分配的。其实DNS也是有分配算法的,可以根据当前连接较少的分配、可以设置Rate权重分配等等,只是目前绝大多数的DNS服务器都不支持;


三、四/七层负载均衡

现代负载均衡技术通常操作于OSI网络模型的第四层或第七层。第四层负载均衡将一个Internet上合法注册的IP地址映射为多个内部服务器的IP地址,对每次TCP连接请求动态使用其中一个内部IP第,达到负载均衡的目的。在第四层交换机中,此中均衡技术得到广泛的应用,一个目标地址是服务器群VIP连接请求的数据包流经交换机,交换机根据源端和目的IP地址、TCP或者UDP端口号和一定的负载策略,在服务器IP和VIP间进行映射,选取服务器群众最好的服务器来处理连接请求。

第七层负载均衡控制应用层服务的内容,提供一种对访问流量的高层控制方式,适合对HTTP服务器群的应用。第七层负载均衡技术通过检查流经的HTTP报头,根据报头内的信息来执行负载均衡任务。


常见四/七层负载均衡设备

  • 硬件负载均衡交换机,代表有F5、array等。
  • 软件负载均衡器,Nginx/LVS/HAProxy是目前使用最广泛的三种负载均衡软件。


软件四/七层负载均衡

1.软件四层负载均衡

代表作为LVS,LVS是一个开源的软件,可以实现linux平台下的简单负载均衡。lvs集群采用IP负载均衡技术和基于内容请求分发技术。调度器具有很好的吞吐率,将请求均衡的转移到不同的服务器上执行,且调度器自动屏蔽掉服务器的故障,从而将一组服务器构成一个高性能的、高可用的虚拟服务器。整个服务器集群的结构对客户是透明的,而且无需修改客户端和服务器端的程序。

nginx负载不平均 nginx负载均衡502_nginx根据参数转发到不同服务器_04

LVS通过修改数据包Ip地址,Mac地址实现负载均衡。

LVS由ipvs(内核中),ipvsadm(用户态)组成。LVS需要理解tcp,ip头部。

当tcp握手信号,SYN数据包达到时,ipvs选择一个后端服务器,将数据包进行转发。在此之后,所有包含相同的ip,tcp头部的数据包都会被转发到之前选择的服务器上。很明显,ipvs无法感知数据包内容。

2.软件的七层负载均衡

大多HTTP反向代理方式,代表产品有nginx、haproxy等,nginx的反向代理负载均衡能够很好的支持虚拟主机,可配置性很强,可以按轮询、IP哈希、URL哈希、权重等多种放肆对后端服务器做负载均衡,同时还支持后端服务器的健康检查。

nginx负载不平均 nginx负载均衡502_nginx根据参数转发到不同服务器_05

nginx负载均衡工作在7层,它会与client、upstream分别建立tcp连接,nginx需要维护这两个连接的状态。

nginx的stream模块可以用于4层负载均衡,主要用于tcp、udp的负载均衡。


nginx负载均衡算法 1.weight权重轮循   

在轮循算法的基础上加上权重,即为权重轮循算法,当使用该算法时,权重和用户访问成正比,权重值越大,被转发的请求也就越多。可以根据服务器的配置和性能指定权重值大小,有效解决新旧服务器性能不均带来的请求分配问题。

upstream myapp1 {
server 172.1.1.103 weight=1;
server 172.1.1.104 weight=2;
}


2.ip_hash  

每个请求按客户端IP 的 hash结果分配,当新的请求到达时,先将其客户端IP通过哈希算法哈希出一个值,在随后的客户端请求中,客户IP 的哈希值只要相同,就会被分配至同一台服务器,该调度算法可以解决动态网页的session 共享问题,但有时会导致请求分配不均,即无法保证1:1 的负载均衡,因为在国内大多数公司都是NAT 上网模式,多个客户端会对应一个外部IP,所以,这些客户端都会被分配到同一节点服务器,从而导致请求分配不均。LVS负载均衡的 -P参数、keepalived配置里的persistence_timeout 50 参数都类似这个Nginx 里的ip_hash 参数,其功能均为解决动态网页的session 共享问题。注意:当负载调度算法为ip_hash时,后端服务器在负载均衡调度中的状态不能有weight 和backup ,即使有也不会生效。

upstream myapp1 {
ip_hash;
server 172.1.1.103;
server 172.1.1.104;
}


3.轮询

按客户端请求顺序把客户端的请求逐一分配到不同的后端节点服务器,如果后端节点服务器宕机(默认情况下nginx只检测80端口)。宕机的服务器会自动从节点服务器池中剔除,以便客户端的用户访问不受影响。新的请求会分配给正产的服务器。

upstream myapp1 {
server 172.1.1.103;
server 172.1.1.104;
}


4.fair

调度算法会根据后端节点服务器的响应时间来分配请求,响应时间短的优先分配。这是更加智能的调度算法。此种算法可以依据页面大小和加载时间长短智能地进行负载均衡,也就是根据后端服务器的响应时间来分配请求,响应时间短的优先分配。Nginx本身是不支持 fair调度算法的,如果需要使用这种调度算法,必须下载Nginx 的相关模块upstream_fair。

upstream myapp1 {
fair;
server 172.1.1.103;
server 172.1.1.104;
}


5.url_hash

按访问URL 的 hash结果来分配请求,使每个URL定向到同一个后端服务器,可以进一步提高后端缓存服务器的效率命中率。(多用于后端服务器为缓存时的场景下)Nginx本身是不支持rul_hash的,如果需要使用这种调度算法,必须安装Nginx 的hash模块软件包。

upstream myapp1 {
server 172.1.1.103;
server 172.1.1.104;
hash $request_uri;
hash_method crc32;
}


6.Nginx负载均衡调度状态

在Nginxupstream模块中,可以设定每台后端服务器在负载均衡调度中的状态,常用的状态有:

  • 1、down,表示当前的server暂时不参与负载均衡
  • 2、backup,预留的备份机器。当其他所有的非backup机器出现故障或者忙的时候,才会请求backup机器,因此这台机器的访问压力最低
  • 3、max_fails,允许请求失败的次数,默认为1,当超过最大次数时,返回proxy_next_upstream模块定义的错误。
  • 4、fail_timeout,请求失败超时时间,在经历了max_fails次失败后,暂停服务的时间。max_fails和fail_timeout可以一起使用。


nginx负载均衡运用场景 1.HTTP负载均衡

upstream模块控制HTTP的负载均衡。这个模块定义了一个目的池,这个目的池可以用IP地址和DNS记录,或者混合使用。

upstream模块模块还定义了任何单个请求是如何分配给任何上游服务器。

在下边的例子中,srv1不参与负载,srv2只有在其他服务器宕机或忙时才参与负载,srv3、srv4参与负载,其中每六次srv3访问五次,srv4一次。所有的请求被代理到服务组node,然后nginx负载均衡的分发请求。

nginx反向代理实现包括下面这些负载均衡HTTP、HTTPS、FastCGI、uwsgi,SCGI和memcached。

要配置HTTPS的负载均衡,只需使用“https”开头的协议。

当要设置FastCGI,uwsgi,SCGI,或者memcached的负载平衡,分别使用fastcgi_pass,uwsgi_pass,scgi_pass和memcached_pass指令。

upstream node {
server 192.9.191.31:8001 down;
server 192.9.191.31:8002 backup;
server www.aa.com:8003 weight=5;
server 192.9.191.31:8004 max_fail=1 fail_timeout=10s; 
}
server{
listen 80;
server_name www.test.com test.com;
location / {
proxy_pass http://node;
}
}


2.TCP、UDP负载均衡

Nginx使用了一个新的stream模块来实现TCP负载均衡,这个模块,类似于http和mail模块,允许我们配置一组监听TCP连接的服务。允许你配置多个服务的TCP连接,通过在upstream的server组中配置proxy_pass指令。

TCP负载均衡支持Nginx原有的调度算法,包括轮询调度,哈希(选择一致)等。同时,调度信息数据也会和健壮性检测模块一起协作,为每个连接选择适当的目标上游服务器。

和其他upstream模块一样,TCP的stream模块也支持自定义负载均和的转发权重(配置weight),还有backup和down的参数,用于踢掉失效的上游服务器。max_conns参数可以限制一台服务器的TCP连接数量,根据服务器的容量来设置恰当的配置数值,尤其在高并发的场景下,可以达到过载保护的目的。

stream{
upstream mysql_read {
server read1.example.com:3306 weight=5;
server read2.example.com:3306;
server 10.10.12.34:3306 backup;
}
server {
listen 3306;
proxy_pass mysql_read;
}
}


案例分享 案例一

后端3台tomcat服务器以2主一备的形式通过2台nginx服务器实现负载均衡,同时nginx通过keepalived实现主备配置

1、地址分配

nginx:172.1.1.106、172.1.1.107

VIP地址:172.1.1.108

tomcatIP:172.1.1.102、172.1.1.103、172.1.1.105

2、应用部署及配置

环境配置及tomcat安装:

安装java环境:

chmod +x jdk-6u16-linux-i586.bin  
./jdk-6u16-linux-i586.bin  
ln -s /usr/local/jdk-1.6.0-16/bin/java /usr/bin/java  
ln -s /usr/local/jdk-1.6.0-16/bin/javac /usr/bin/javac  
ln -s /usr/local/jdk-1.6.0-16 /usr/local/jdk  
ln -s /usr/local/jdk-1.6.0-16/jre /usr/local/jre  
tar zxvf apache-tomcat-6.0.20.tar.gz  
mv apache-tomcat-6.0.20 /usr/local/tomcat

设置环境变量:

JAVA_HOME=/usr/local/jdk  
export JAVA_HOME  
JRE_HOME=/usr/local/jre  
export JRE_HOME  
CLASSPATH=/usr/local/tomcat/common/lib/:/usr/local/jdk/lib:/usr/local/jre/lib  
export CLASSPATH  
PATH=$PATH:/usr/local/tomcat/bin/:/usr/local/apache/bin:/usr/local/jdk/bin:/usr/local/jre/bin  
export PATH  
TOMCAT_HOME=/usr/local/tomcat  
export TOMCAT_HOME
nginx安装及配置

下载及安装nginx:

wget ftp://ftp.csx.cam.ac.uk/pub/software/programming/pcre/pcre-7.7.tar.gz  
tar zxvf pcre-7.7.tar.gz  
cd pcre-7.7/  
./configure  
make && make install  
wget http://sysoev.ru/nginx/nginx-0.7.17.tar.gz  
tar zxvf nginx-0.7.17.tar.gz  
cd nginx-0.7.17/  
./configure --prefix=/usr/local/nginx 
make && make install

nginx.conf配置文件,两台nginx服务器保持一致

user nobody nobody;  
worker_processes 1;  
pid /usr/local/nginx/logs/nginx.pid;  
worker_rlimit_nofile 51200;  
events  
{  
use epoll;  
worker_connections 51200;  
}  
http{  
include       mime.types;  
default_type application/octet-stream;  
server_names_hash_bucket_size 128;  
client_header_buffer_size 32k;  
large_client_header_buffers 4 32k;  
client_max_body_size 8m;  
sendfile on;  
tcp_nopush     on;  
keepalive_timeout 60;  
tcp_nodelay on;  
fastcgi_connect_timeout 300;  
fastcgi_send_timeout 300;  
fastcgi_read_timeout 300;  
fastcgi_buffer_size 64k;  
fastcgi_buffers 4 64k;  
fastcgi_busy_buffers_size 128k;  
fastcgi_temp_file_write_size 128k;  
gzip on;  
gzip_min_length 1k;  
gzip_buffers     4 16k;  
gzip_http_version 1.0;  
gzip_comp_level 2;  
gzip_types       text/plain application/x-javascript text/css application/xml;  
gzip_vary on;  
upstream node
{  
server 172.1.1.102:8080;  
server 172.1.1.103:8080;  
server 172.1.1.105:8080 backup;  
}  
server {  
listen 80;  
server_name test.com;  
location / {  
root /var/www ;  
index index.jsp index.htm index.html;  
proxy_redirect off;  
proxy_set_header Host $host;  
proxy_set_header X-Real-IP $remote_addr;  
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;  
proxy_pass http://node;  
}  
log_format access '$remote_addr - $remote_user [$time_local] "$request" ' 
'$status $body_bytes_sent "$http_referer" ' 
'"$http_user_agent" $http_x_forwarded_for';  
access_log /var/log/access.log access;  
}  
}
Nginx服务器双机部署

Keepalived下载安装:

wget http://www.keepalived.org/software/keepalived-1.1.15.tar.gz  

http://www.keepalived.org/software/keepalived-1.2.13.tar.gz

tar zxvf keepalived-1.1.15.tar.gz  

cd keepalived-1.1.15  

./configure  

make  

make install  

cp /usr/local/etc/rc.d/init.d/keepalived /etc/rc.d/init.d/  

cp /usr/local/etc/sysconfig/keepalived /etc/sysconfig/  

mkdir /etc/keepalived  

cp /usr/local/etc/keepalived/keepalived.conf /etc/keepalived/  

cp /usr/local/sbin/keepalived /usr/sbin/  

service keepalived start|stop 

两台nginx服务器上keepalived配置文件

172.1.1.106主机:

ip avrrp_instance VI_INET1 {  
        state MASTER  
        interface eth0  
        virtual_router_id 53  
        priority 200  
        advert_int 1  
        authentication {  
                auth_type pass  
                auth_pass yourpass  
        }  
        virtual_ipaddress {  
                172.1.1.108  
        }  
}

172.1.1.107主机:

vrrp_instance VI_INET1 {  
        state BACKUP  
        interface eth0  
        virtual_router_id 53  
        priority 100  
        advert_int 1  
        authentication {  
                auth_type pass  
                auth_pass yourpass  
        }  
        virtual_ipaddress {  
                172.1.1.108  
        }  
}

以上步骤即可完成nginx主备下的负载均衡配置,具体负载策略大家可以根据现场环境实际情况进行调整,配置仅供参考。


案例二

在流量陡增的互联网面前,接口限流也是很有必要的,尤其是针对高并发的场景。Nginx的限流主要是两种方式:限制访问频率和限制并发连接数。该案例就是一个通过限制单个IP并发连接数防止后端服务器过载的例子。

  1. 维护人员接到告警发现tuxedo中间件多个服务出现堵塞,尝试扩服务或重启均无法解决。

nginx负载不平均 nginx负载均衡502_nginx 负载均衡配置_06

  1. 对比业务量情况,发现同比环比均超30%以上

nginx负载不平均 nginx负载均衡502_nginx根据参数转发到不同服务器_07

nginx负载不平均 nginx负载均衡502_nginx 负载均衡 找不到css_08

  1. 随即调整nginx限流策略,配置及注释如下
http {
   include      mime.types;
   default_type application/octet-stream;
   proxy_intercept_errors on;
   #log_format main  '$remote_addr - $remote_user [$time_local] "$request" '
   #                 '$status $body_bytes_sent "$http_referer" '
   #                 '"$http_user_agent" "$http_x_forwarded_for"';
   #access_log logs/access.log  main;
   log_format main  '$remote_addr - $remote_user [$time_local] "$request" '
                     '$status $body_bytes_sent "$http_referer" '
                     '"$http_user_agent" "$http_x_forwarded_for"';
   log_format acc "$remote_addr $remote_user [$time_local] $request_method $uri $args $server_protocol"
                  " $status $body_bytes_sent $request_time $http_referer $upstream_addr $upstream_cache_status"
                  " $upstream_status $http_x_forwarded_for $cookie_route $http_host $http_user_agent <> <> ";
   access_log logs/access_acc$hostname$year$month$day.log acc ;
   sendfile       on;
   tcp_nopush     on;
   keepalive_timeout 65;
   client_header_timeout 60;
   client_body_timeout 60;
   reset_timedout_connection on;
   send_timeout 60;
   server_tokens off;
   proxy_next_upstream    error timeout;
   proxy_set_header       Host $http_host;    
   proxy_set_header       X-Real-IP $http_x_forwarded_for;
   proxy_set_header       X-Forwarded-For $proxy_add_x_forwarded_for;
   proxy_connect_timeout  65;
   proxy_send_timeout     120;
   proxy_read_timeout     120;
   proxy_buffer_size      8k;
   proxy_buffers          8 64k;
   proxy_busy_buffers_size 128k;
   proxy_temp_file_write_size 128k;
   proxy_http_version 1.1;  
 limit_conn_zone $binary_remote_addr zone=one:10m;
##定义一个10M的名字为one的内存区域,用于记录访问频次;
   upstream dt_cluster {
       server 172.1.1.230:17001;
       server 172.1.1.230:17002;
       server 172.1.1.230:17003;
       server 172.1.1.230:17004;
       keepalive 32;
       check interval=5000 rise=2 fall=3 timeout=10000;
     }
   lua_package_path "/usr/local/nginx/lua/lua-resty-redis/lib/?.lua;;";
   #gzip on;
   server {
       listen      7001;
       listen      7002;
       listen      7003;
       listen      7004;
       #server_name localhost;
       #charset koi8-r;
       #access_log logs/host.access.log  main;
       set $resp_body "";      
                               location /OpenEbus {
                                              proxy_pass http://dt_cluster;
                                 #              include body_filter_by_lua.conf;
                                 #               lua_need_request_body on;
                                 #               body_filter_by_lua '
                                 #                              local resp_body = string.sub(ngx.arg[1], 1, 1000)
                                 #                              ngx.ctx.buffered  = (ngx.ctx.buffered or "") .. resp_body
                                 #                              if ngx.arg[2] then
                                 #                                     ngx.var.resp_body = ngx.ctx.buffered
                                 #                              end
                                 #               ';
limit_conn one  250;
##限制并发连接最多250个;
}
                               location /hello {
                                                default_type 'text/plain';
                                                content_by_lua 'ngx.say("hello, lua")';
                                       }
location /mockService {
                       proxy_pass http://dt_cluster;
                    }
       location / {
                         return 403;
                                       }      
   }
}

通过使用limit_conn_zone 和limit_conn 对某个目录或指定后缀,比如.html或.jpg进行并发连接限制。有了连接数限制,相当于限制了客户端浏览器和Nginx之间的管道个数,浏览器通过管道运输请求,如同向自来水管中放水,水的流速和压力对于管道另外一端是有影响的。为了防止不信任的客户端通过这个管道疯狂发送请求,消耗CPU内存等资源,以及防止URL不断发出连接请求,必须对请求的速度进行限制,如同对水流速度限制一样。