【转载】负载均衡Load Balance学习

转载于 http://www.javashuo.com/article/p-kzfudmri-bm.htmlphp

 

目录html

1. 负载均衡简介
2. 负载均衡算法
3. Nginx负载均衡调度算法源码调研

 

1. 负载均衡简介前端

0x1: 负载均衡是什么linux

负载均衡是一种技术架构方法,它并非具体指哪种技术,也正是由于这样,负载均衡被运用在了不少的领域nginx

严格来讲,负载平衡(Load balancing)是一种计算机网络技术,用来在多个计算机(计算机集群)、网络链接、CPU、磁盘驱动器或其余资源中分配负载,以达到最佳化资源使用、最大化吞吐率、最小化响应时间、同时避免过载的目的,使用带有负载平衡的多个服务器组件,取代单一的组件,能够经过冗余提升可靠性。负载平衡服务一般是由专用软体和硬件来完成git

负载平衡最重要的一个应用是利用多台服务器提供单一服务,这种方案有时也称之为"服务器农场"。一般,负载平衡主要应用于github

复制代码
1. Web网站
2. 大型的Internet Relay Chat网络
3. 高流量的文件下载网站
4. NNTP(Network News Transfer Protocol)服务
5. DNS服务
6. 海量SOCKET、HTTP长链接(常见于页游、MSN聊天软件、APP等场景中)
7. 数据库链接、查询服务(称之为数据库负载平衡器)
复制代码

对于互联网服务,负载平衡器一般是一个软体程序,这个程序侦听一个外部端口,互联网用户能够经过这个端口来访问服务,而做为负载平衡器的软体会将用户的请求转发给后台内网服务器,内网服务器将请求的响应返回给负载平衡器,负载平衡器再将响应发送到用户,这样就向互联网用户隐藏了内网结构,阻止了用户直接访问后台(内网)服务器,使得服务器更加安全,能够阻止对核心网络栈和运行在其它端口服务的攻击
当全部后台服务器出现故障时,有些负载平衡器会提供一些特殊的功能来处理这种状况。例如转发请求到一个备用的负载平衡器、显示一条关于服务中断的消息等。负载平衡器使得IT团队能够显著提升容错能力。它能够自动提供大量的容量以处理任何应用程序流量的增长或减小算法

0x2: 负载均衡的类别数据库

咱们知道,负载均衡是一种架构思想,它在不少业务场景下都有获得运用,大致上,负载均衡有以下几种apache

复制代码
1. 面向应用层的负载均衡器
    1.1 WEB访问
        1) Apache模块mod_proxy(提供代理服务器功能)
        http://www.php100.com/manual/apache2/mod/mod_proxy.html
        此模块实现了Apache的代理/网关。它实现了如下规范的代理:
            1.1) AJP13(Apache JServe Protocol v1.3): mod_proxy_ajp
            1.2) FTP: mod_proxy_ftp
            1.3) CONNECT(用于SSL): mod_proxy_connect
            1.4) HTTP/0.9: mod_proxy_http
            1.5) HTTP/1.0: mod_proxy_http
            1.6) HTTP/1.1: mod_proxy_http
        此模块经配置后可用上述或其它协议链接其它代理模块
        2) Apache Web服务器的mod_proxy_balancer扩展(提供负载均衡功能)
        在使用基于Apache的负载均衡配置的时候,须要同时开启mod_proxy、mod_proxy_balancer、mod_proxy_xx这些模块
        
        3) 基于反向代理的Nginx负载均衡器
        http://blog.sina.com.cn/s/blog_9c3ba23d01010rof.html

        4) Varnish负载均衡
        http://network.51cto.com/art/201005/198191.htm

        5) Pound反向代理和负载均衡器

        6) 基于URL Rewrite的负载均衡器

    1.2 DNS查询
        1) 基于DNS轮询的负载均衡: 
        用于将CLINET的DNS解析请求按照必定的算法均衡地分配到一个IP集群中,针对WEB URL访问提供负载均衡,即将一个域名解析到多个IP地址,这种方法可能存在的问题是,轮叫DNS方法的调度粒度是基于每一个域名服务器的,
域名服务器对域名解析的缓存会妨碍轮叫解析域名生效,这会致使服务器间负载的不平衡 2. 面向传输层的负载均衡器 1) HAProxy HAProxy提供高可用性、负载均衡以及基于TCP和HTTP应用的代理,支持虚拟主机 3. 面向网络层的负载均衡器 3.1 LVS(Linux Virtual Server) 1) Virtual Server via Network Address Translation(VS/NAT) 经过网络地址转换,调度器重写请求报文的目标地址,根据预设的调度算法,将请求分派给后端的真实服务器;真实服务器的响应报文经过调度器时,报文的源地址被重写,再返回给客户,完成整个负载调度过程。 2) Virtual Server via IP Tunneling(VS/TUN) 采用NAT技术时,因为请求和响应报文都必须通过调度器地址重写,当客户请求愈来愈多时,调度器的处理能力将成为瓶颈。为了解决这个问题,调度器把请求报文经过IP隧道转发至真实服务器,而真实服务器将响应直接返回给
客户,因此调度器只处理请求报文。因为通常网络服务应答比请求报文大许多,采用VS/TUN技术后,集群系统的最大吞吐量能够提升10倍 3) Virtual Server via Direct Routing(VS/DR) VS/DR经过改写请求报文的MAC地址,将请求发送到真实服务器,而真实服务器将响应直接返回给客户。同VS/TUN技术同样,VS/DR技术可极大地提升集群系统的伸缩性。这种方法没有IP隧道的开销,对集群中的真实服务器也
没有必须支持IP隧道协议的要求,可是要求调度器与真实服务器都有一块网卡连在同一物理网段上 3. 多层负载均衡: 高性能系统一般会使用 对于一个多层次架构体系,在负载均衡器或网络分发器后面有两种设计,术语称之为Bowties和Stovepipes 1) Stovepipe 事务是从顶部分发的,而后从一个固定通道经过一系列硬件和软件设备,到达最终目的地 2) Bowties 在每一层中事务处理有多条路径可供选择 在事务处理的网络结构中可能会是Stovepipes,也能够是Bowties,或者根据每一层的实际需求采用杂货构架 4. 跨语言异构系统间通讯负载均衡调度器 1) Gearman 使用Gearman将合适的计算任务分发给多台计算机,如此大量的任务就能够更快的完成了 http://baike.baidu.com/view/2926980.htm?fr=aladdin http://blog.chinaunix.net/uid-20357359-id-1963682.html 对于Gearman(齿轮工)的技术架构,咱们须要重点理解几点 1.1) Gearman是一种API级的跨系统通讯方式,这种方式和REST、RPC相比提升了耦合度 1.2) 从某种程度上说,Gearman和数据库技术中的存储过程的思想很相似,经过在预处理阶段实现后端的实现逻辑,并向前端暴露出API级的调用接口,这种方式提供了API的准确性、定制性、安全性 1.3) client、job schedule server、worker之间的API通讯是基于网络IO实现的
复制代码

0x3: 负载均衡的特性

当前,负载均衡器有各类各样的"工做排程算法"(用于决定将前端用户请求发送到哪个后台服务器),这是负载均衡最核心的思想,负载均衡器至关因而在原始网络链路上的中间插入一个"Proxy",这个Proxy能够位于网络架构上的任何一个位置,用于将原来的"1:N"的性能瓶颈关系转化为"N:N"关系,只要是遵循了这个思想的架构和方案,均可以说是实现了负载均衡的架构,从某种程度上来讲,负载均衡器就是反向代理服务器,负载转发并重创建原始链接

不管是软件负载均衡器,仍是硬件负载均衡器都有一系列的特性

复制代码
1. 不对称负载调节
能够对后台服务器设置权重因子,权重因子用于控制服务器的请求处理量,进而控制服务器的负载。当后台服务器的处理能力不是等同的时候,这是一种控制服务器负载的简单方法

2. 优先启动
当出现故障的服务器达到某个阀值,或者服务器负载太高时,备用服务器必需能够及时上线提供服务

3. SSL截断和加速(SSL卸载)
依赖服务器负载,处理加密数据或者经过SSL进行的受权请求会消耗Web服务器的大量CPU,随着需求增长用户会明显感受到响应时间变长。为了消除Web服务器上这部分(处理加密)负载,负载均衡器可能会将SSL通信截断在负载均衡器上。
有些硬件负载均衡器上包含有专门用于处理SSL的硬件。当负载均衡器截断SSL链接请求,再将用户请求转发给后台前将HTTPS变为HTTP。只要负载均衡器不超载,这个特性不会影响用户体验。 这种方法的负面效应是,因为全部SSL都由负载均衡器一台设备来处理,它会致使负载均衡器成为负载均衡体系的一个瓶颈。若是不使用这个特性,SSL请求将会分发给各个Web服务器处理。是否采用这一特性,须要分析比较二者的资金投
入状况,含有处理SSL特殊硬件的负载均衡器一般价格高昂,而Web服务器通常比较廉价。增长少许的Web服务器的花费可能明显比升级负载均衡器要少。另外,一些服务器厂商如Oracle/Sun也开始在它们的CPU中加入加密加速模块,
例如T2000。在负载均衡器上截断SSL的另外一个优势是容许负载均衡器能够对基于HTTPS请求数据进行负载均衡或内容交换 4. DDOS攻击防御 负载均衡器能够提供例如SYN cookies特性和延时绑定(在TCP握手完成以前,后台服务器不会与用户通信)来减缓SYN flook攻击,and generally offload work from the servers to a more efficient platform 5. HTTP压缩 使用gzip压缩HTTP数据,以减小网络上的数据传输量。对于响应时间较长,传输距离较远的用户,这一特性对于缩短响应时间效果明显。这个特性会要求更多一些的负载均衡器CPU,这一功能也能够由Web服务器来完成 6. TCP offload 其主要思想是同样的,一般每一个用户的每一个请求都会使用一个不一样的TCP链接,这个特性利用HTTP/1.1未来自多个用户的多个请求合并为单个TCP socket再转发给后台服务器,即咱们常说的TCP缓存组包技术 7. TCP缓冲 负载均衡器能够暂存后台服务器对客户的响应数据,再将它们转发给那些响应时间较长网速较慢的客户,如此后台Web服务器就能够释放相应的线程去处理其它任务如直接整个响应数据直接发送给网速较快的用户,这能够从必定程度上解
决"HTTP SLOW ATTACK"慢速攻击 8. 后台服务器直接响应用户(Direct Server Return) 这是不对称负载分布的一项功能,在不对称负载分布中请求和回应经过不一样的网络路径 9. 服务器健康检查 负载均衡器能够检查后台服务器应用层的健康情况并从服务器池中移除那些出现故障的服务器 10. HTTP缓存 负载均衡器能够存储"静态内容"(相比于静态内容,动态脚本的执行的缓存须要慎重考虑),当用户请求它们时能够直接响应用户而没必要再向后台服务器请求 11. 内容过滤 负载均衡器能够按要求修改经过它的数据 12. HTTP安全 负载均衡器能够隐藏HTTP出错页面,删除HTTP响应头中的服务器标示信息,加密cookies以防止用户修改 13. 优先队列 也可称之为流量控制。它能够对不一样的内容设定不一样的优先级 14. Content-aware switching(内容感知开关) 负载均衡器能够基于用户请求的URL发送请求到不一样的后台服务器,不管内容是加密(HTTPS)仍是没有加密(HTTP) 15. 用户受权 对来自不一样身份验证源的用户进行验证,而后再容许他们访问一个网站 16. 可编程的流量控制 负载均衡器容许使用脚本编程来定制负载均衡方法,任意的流量控制以及其它功能 17. 防火墙功能 因为安全的缘由,不容许用户直接访问后台服务器。防火墙是由一系列规则构成,它们决定着哪些请求能够经过一个接口而哪些不被容许。 提供入侵阻止功能。在防火墙保障网络层/传输层安全的基础上,提供应用层安全防范
复制代码

 

2. 负载均衡算法

最简单的是随机选择和轮询。更为高级的负载均衡器会考虑其它更多的相关因素,如后台服务器的负载,响应时间,运行状态,活动链接数,地理位置,处理能力,或最近分配的流量

复制代码
1. 针对WEB访问请求的负载均衡调度策略(反向代理的网络链接方式)
    1) RR(Round Robin)
    每一个请求按时间顺序逐一分配到不一样的后端服务器,若是后端服务器down掉,能自动剔除,在实现RR算法的时候,还须要根据其余的因素: max_fails、fail_timeout、weight等因素进行综合评估
    
    2) WEIGHT(权重) 
    指定轮询概率,WEIGHT和访问比率成正比,用于后端服务器性能不均的状况

    3) IP_HASH 
    每一个请求按访问IP的Hash结果分配,这样每一个访客固定访问一个后端服务器,能够解决"负载集群SESSION同步"的问题(由于Hash具备结果一致性)

    4) Fair(第三方) 
    按后端服务器的响应时间来分配请求,响应时间短的优先分配 

    5) URL_HASH(第三方)
    按访问URL的Hash结果来分配请求,使每一个URL定向到同一个后端服务器,后端服务器为缓存时比较有效

2. 针对网络IP层访问请求的负载均衡调度策略(面向SOCKET的IP负载均衡)
    1) 轮叫(Round Robin)
    调度器经过"轮叫"调度算法将外部请求按顺序轮流分配到集群中的真实服务器上,它均等地对待每一台服务器,而无论服务器上实际的链接数和系统负载

    2) 加权轮叫(Weighted Round Robin)
    调度器经过"加权轮叫"调度算法根据真实服务器的不一样处理能力来调度访问请求。这样能够保证处理能力强的服务器处理更多的访问流量。调度器能够自动问询真实服务器的负载状况,并动态地调整其权值。

    3) 最少连接(Least Connections)
    调度器经过"最少链接"调度算法动态地将网络请求调度到已创建的连接数最少的服务器上。若是集群系统的真实服务器具备相近的系统性能,采用"最小链接"调度算法能够较好地均衡负载。

    4) 加权最少连接(Weighted Least Connections)
    在集群系统中的服务器性能差别较大的状况下,调度器采用"加权最少连接"调度算法优化负载均衡性能,具备较高权值的服务器将承受较大比例的活动链接负载。调度器能够自动问询真实服务器的负载状况,并动态地调整其权值。

    5) 基于局部性的最少连接(Locality-Based Least Connections)
    "基于局部性的最少连接" 调度算法是针对目标IP地址的负载均衡,目前主要用于Cache集群系统。该算法根据请求的目标IP地址找出该目标IP地址最近使用的服务器,若该服务器 是可用的且没有超载,将请求发送到该服务器;若服
务器不存在,或者该服务器超载且有服务器处于一半的工做负载,则用"最少连接"的原则选出一个可用的服务器,将请求发送到该服务器。 6) 带复制的基于局部性最少连接(Locality-Based Least Connections with Replication) "带复制的基于局部性最少连接"调度算法也是针对目标IP地址的负载均衡,目前主要用于Cache集群系统。它与LBLC算法的不一样之处是它要维护从一个目标IP地址到一组服务器的映射,而LBLC算法维护从一个目标IP地址到一台服务
器的映射。该算法根据请求的目标IP地址找出该目标IP地址对应的服务 器组,按"最小链接"原则从服务器组中选出一台服务器,若服务器没有超载,将请求发送到该服务器,若服务器超载;则按"最小链接"原则从这个集群中选出一台服
务器,将该服务器加入到服务器组中,将请求发送到该服务器。同时,当该服务器组有一段时间没有被修改,将最忙的服务器从服务器组中删除,以下降复制的 程度。 7) 目标地址散列(Destination Hashing) "目标地址散列"调度算法根据请求的目标IP地址,做为散列键(Hash Key)从静态分配的散列表找出对应的服务器,若该服务器是可用的且未超载,将请求发送到该服务器,不然返回空。 8) 源地址散列(Source Hashing) "源地址散列"调度算法根据请求的源IP地址,做为散列键(Hash Key)从静态分配的散列表找出对应的服务器,若该服务器是可用的且未超载,将请求发送到该服务器,不然返回空
复制代码

Relevant Link:

http://blog.sina.com.cn/s/blog_9c3ba23d01010rof.html
http://zh.wikipedia.org/wiki/%E8%B4%9F%E8%BD%BD%E5%9D%87%E8%A1%A1_(%E8%AE%A1%E7%AE%97%E6%9C%BA)
http://www.linuxvirtualserver.org/zh/lvs1.html#7

 

3. Nginx负载均衡调度算法源码调研 

nginx 的负载均衡策略能够划分为两大类:内置策略和扩展策略。内置策略包含加权轮询和ip hash,在默认状况下这两种策略会编译进nginx内核,只需在nginx配置中指明参数便可。扩展策略有不少,如fair、通用hash、 consistent hash等,默认不编译进nginx内核,是第三方模块

在开始学习调度算法以前,咱们须要先了解一下Nginx调度算法所涉及到的数据结构,Nginx的负载均衡是根据配置文件进行的,因此Nginx在初始化的时候须要从配置文件中读取配置并保存到相应的结构体中

1. struct ngx_conf_t

\nginx-1.0.14_comment-master\src\core\ngx_core.h

typedef struct ngx_conf_s        ngx_conf_t;

\nginx-1.0.14_comment-master\src\core\ngx_conf_file.h

复制代码
struct ngx_conf_s 
{
    char                 *name;  //没有使用
    ngx_array_t          *args;  //指令的参数

    ngx_cycle_t          *cycle; //指向系统参数,在系统整个运行过程当中,
                                 //须要使用的一些参数、资源须要统一的管理
    ngx_pool_t           *pool;  //内存池
    ngx_pool_t           *temp_pool; //分配临时数据空间的内存池
    ngx_conf_file_t      *conf_file; //配置文件的信息
    ngx_log_t            *log; //日志

    void                 *ctx;  //模块的配置信息
    ngx_uint_t            module_type; //当前指令的类型
    ngx_uint_t            cmd_type; //命令的类型

    ngx_conf_handler_pt   handler; //指令处理函数,有本身行为的在这里实现
    char                 *handler_conf; //指令处理函数的配置信息
};
复制代码

2. struct ngx_http_upstream_srv_conf_t

\nginx-1.0.14_comment-master\src\http\ngx_http_upstream.h

typedef struct ngx_http_upstream_srv_conf_s  ngx_http_upstream_srv_conf_t;

\nginx-1.0.14_comment-master\src\http\ngx_http_upstream.h

复制代码
struct ngx_http_upstream_srv_conf_s 
{
    ngx_http_upstream_peer_t         peer;
    void                           **srv_conf;  // 在ngx_http_upstream()函数中被设置,指向的是本层的srv_conf  

    ngx_array_t                     *servers;   // ngx_http_upstream_server_t

    ngx_uint_t                       flags;     // 调用函数时ngx_http_upstream_add() 指定的标记  
    ngx_str_t                        host;      // 在函数 ngx_http_upstream_add()中设置(e.g. upstream backend中的backend)
    u_char                          *file_name; // "/usr/local/nginx/conf/nginx.conf"  
    ngx_uint_t                       line;      // proxy在配置文件中的行号  
    in_port_t                        port;      // 使用的端口号,ngx_http_upstream_add()函数中添加, 指向ngx_url_t-->port,一般在函数ngx_parse_inet_url()中解析
    in_port_t                        default_port;  //默认使用的端口号(ngx_http_upstream_add()函数中添加, 指向ngx_url_t-->default_port)
};
复制代码

3. struct ngx_http_upstream_peer_t

\nginx-1.0.14_comment-master\src\http\ngx_http_upstream.h

复制代码
typedef struct 
{
    //使用负载均衡的类型,默认采用ngx_http_upstream_init_round_robin()
    ngx_http_upstream_init_pt        init_upstream;

    //使用的负载均衡类型的初始化函数 
    ngx_http_upstream_init_peer_pt   init;

    //us->peer.data = peers; 指向的是 ngx_http_upstream_rr_peers_t(函数 ngx_http_upstream_init_round_robin()中设置)
    void                            *data;
} ngx_http_upstream_peer_t;
复制代码

ngx_http_upstream_init_peer_pt、ngx_http_upstream_init_pt 是函数指针类型

\nginx-1.0.14_comment-master\src\http\ngx_http_upstream.h

typedef ngx_int_t (*ngx_http_upstream_init_pt)(ngx_conf_t *cf, ngx_http_upstream_srv_conf_t *us);
typedef ngx_int_t (*ngx_http_upstream_init_peer_pt)(ngx_http_request_t *r, ngx_http_upstream_srv_conf_t *us);

4. struct ngx_array_t 

\nginx-1.0.14_comment-master\src\core\ngx_core.h

typedef struct ngx_array_s       ngx_array_t;

\nginx-1.0.14_comment-master\src\core\ngx_array.h

复制代码
// 动态数组
struct ngx_array_s 
{
    // elts指向数组的首地址
    void        *elts; 
    // nelts是数组中已经使用的元素个数
    ngx_uint_t   nelts; 
    // 每一个数组元素占用的内存大小
    size_t       size;  
    // 当前数组中可以容纳元素个数的总大小
    ngx_uint_t   nalloc; 
    // 内存池对象
    ngx_pool_t  *pool;  
};
复制代码

5. struct ngx_http_upstream_server_t

\nginx-1.0.14_comment-master\src\http\ngx_http_upstream.h
须要注意的是,配置文件中出现的参数只能和某些策略配合使用,因此若是发现某参数没有生效,则应该检查这一点。在配置解析的过程当中,这些选项设置都被转换为Nginx内对于的变量值,对应的结构体ngx_http_upstream_server_t以下

复制代码
typedef struct 
{
    ngx_addr_t                      *addrs;         //指向存储IP地址的数组的指针,host信息(对应的是 ngx_url_t->addrs )  
    ngx_uint_t                       naddrs;        //与第一个参数配合使用,数组元素个数(对应的是 ngx_url_t->naddrs )  
    ngx_uint_t                       weight;
    ngx_uint_t                       max_fails;
    time_t                           fail_timeout;

    unsigned                         down:1;
    unsigned                         backup:1;
} ngx_http_upstream_server_t;
复制代码

6. struct ngx_http_upstream_rr_peer_t

\nginx-1.0.14_comment-master\src\http\ngx_http_upstream_round_robin.h

复制代码
typedef struct 
{
    struct sockaddr                *sockaddr;        //后端服务器地址  
    socklen_t                       socklen;        //后端服务器地址长度
    ngx_str_t                       name;        //后端名称  
    ngx_str_t                       server;

    ngx_int_t                       current_weight;    //当前权重,nginx会在运行过程当中调整此权重  
    ngx_int_t                       effective_weight;    //配置的权重
    ngx_int_t                       weight;        //current_weight是运行时的动态权值

    ngx_uint_t                      fails;        //已尝试失败次数  
    time_t                          accessed;        //检测失败时间,用于计算超时  
    time_t                          checked;

    ngx_uint_t                      max_fails;        //最大失败次数  
    time_t                          fail_timeout;    //多长时间内出现max_fails次失败便认为后端down掉了  

    ngx_uint_t                      down;          /* unsigned  down:1; */

#if (NGX_HTTP_SSL)
    ngx_ssl_session_t              *ssl_session;   /* local to a process */
#endif
} ngx_http_upstream_rr_peer_t;
复制代码

列表最前面须要带有一些head信息,用结构体ngx_http_upstream_rr_peers_t与之对应

7struct ngx_http_upstream_rr_peers_t

\nginx-1.0.14_comment-master\src\http\ngx_http_upstream_round_robin.h

复制代码
typedef struct ngx_http_upstream_rr_peers_s  ngx_http_upstream_rr_peers_t;

struct ngx_http_upstream_rr_peers_s 
{
    ngx_uint_t                      single;         // unsigned  single:1;  
    ngx_uint_t                      number;         // 队列中服务器数量 
    ngx_uint_t                      last_cached;

 /* ngx_mutex_t                    *mutex; */
    ngx_connection_t              **cached;

    ngx_str_t                      *name;

    ngx_http_upstream_rr_peers_t   *next;           // 后备服务器列表挂载在这个字段下  

    ngx_http_upstream_rr_peer_t     peer[1];
};
复制代码

0x1: 带权重的RR调度策略

图片很大,能够另存到本地看

能够看到,Weight RR加权轮叫的流程简单来讲能够归纳为: 读取配置文件->保存配置文件中的指定选项->创建后端服务器集群的数据结构,并为止设置参数->根据权重动态计算公式在每次请求到达时对每一个后端服务器的权值进行动态更新,每次都选出一个最优权值服务器进行调度

0x2: Nginx的负载均衡权重动态调整算法

在ngx_http_upstream_get_peer()中传入的参数是一个ngx_http_upstream_rr_peer_t结构体

复制代码
typedef struct 
{
    ...
    /*
    1. 当前权重,nginx会在运行过程当中调整此权重  
    current_weight至关于重量,是运行时的动态权值,它的变化基于effective_weight。可是
        1) effective_weight在其对应的peer服务异常时,会被调低
        2) 当服务恢复正常时,effective_weight会逐渐恢复到实际值(配置的weight)
    */
    ngx_int_t    current_weight;         

    /*
    2. 配置的权重
    effective_weight至关于质量(来源于配置的weight),反应了本质
    */
    ngx_int_t    effective_weight;     
    ngx_int_t    weight;            //current_weight是运行时的动态权值
    ...
 } ngx_http_upstream_rr_peer_t;
复制代码

weight、effective_weight都是初始化为配置项中的weight值。current_weight初始化为0

1. weight的值在整个运行过程当中不发生变化
2. total变量记录了针对一个服务列表的一次轮询过程当中轮询到的全部服务的effective_weight总和。在每一次针对服务列表的轮询以前会置为为0
3. 遍历服务列表的过程当中,每遍历到一个服务
    1) 会在该服务的current_weight上加上其对应的effective_weight。这个是累加
    2) 若是对统一的服务列表进行另外一次轮询,那么会在前面计算的current_weight的基础之上再加上effective_weight
4. 轮询策略是取current_weight最大的服务器。每次取到后端服务(用best表示)后,都会把该对象peer的current_weight减去total的值。由于该服务刚被选中过,所以要下降权值

关于effective_weight的变化,有两处

复制代码
1. ngx_http_upstream_get_peer()中
//服务正常,effective_weight 逐渐恢复正常      
if (peer->effective_weight < peer->weight) 
{  
    peer->effective_weight++;  
}

2. 另外一处是在释放后端服务的函数ngx_http_upstream_free_round_robin_peer中
if (peer->max_fails) 
{  
     //服务发生异常时,调低effective_weight  
    peer->effective_weight -= peer->weight / peer->max_fails;  
}
复制代码

权重高的会优先被选中,并且被选中的频率也更高。权重低的也会因为权重逐渐增加得到被选中的机会

0x3: IP_HASH调度策略

IP_hash和RR 的策略有两点不一样在于: 当一个客户请求到nginx后

1. Nginx如何选择一个最初的server
2. 以及当前选择的server不能提供服务时,如何选择下一个server

在IP_hash策略中

复制代码
1. 它选择最初的server的方法是根据请求客户端的IP计算出一个哈希值,再根据哈希值选择后台的服务器,由IP计算哈希值的算法以下
//其中公式中hash初始值为89,iphp->addr[i]表示客户端的IP,经过三次哈希计算得出一个IP的哈希值
for (i = 0; i < 3; i++) 
{
      hash = (hash * 113 + iphp->addr[i]) % 6271;
}
 
2. 在选择下一个server时,ip_hash的选择策略是这样的:
它在上一次哈希值的基础上,再次哈希,就会获得一个全新的哈希值,再根据哈希值选择另一个后台的服务器。哈希算法仍然是
for (i = 0; i < 3; i++) 
{
      hash = (hash * 113 + iphp->addr[i]) % 6271;
}
 
3. 在这种ip_hash策略
    1) 若是一个后台服务器不能提供提服务(链接超时或读超时),该服务器的失败次数就会加一,当一个服务器的失败次数达到max_fails所设置的值,就会在fail_timeout所设置的时间段内不能对外提供服务,这点和RR是一致的
    2) 若是当前server不能提供服务,就会根据当前的哈希值再哈希出一个新哈希值,选择另外一个服务器继续尝试,尝试的最大次是upstream中server的个数,若是server的个数超过20,也就是要最大尝试次数在20次以上,当尝试
次数达到20次,仍然找不到一个合适的服务器,ip_hah策略再也不尝试ip哈希值来选择server, 而在剩余的尝试中,它会转而使用RR的策略,使用轮循的方法,选择新的server。 4. 除了以上部分不一样外,IP_hash的其他部分和RR彻底同样,由于它的其他部分功能的实现都是经过调用RR中的函数
复制代码

Relevant Link:

复制代码
http://www.cnblogs.com/yjf512/archive/2012/06/13/2548515.html
https://github.com/jianfengye/nginx-1.0.14_comment
http://blog.csdn.net/gzh0222/article/details/7996835
http://blog.csdn.net/livelylittlefish/article/details/6571497
http://blog.sina.com.cn/s/blog_9c3ba23d01010rof.html
http://blog.csdn.net/xiajun07061225/article/details/9318871
http://baidutech.blog.51cto.com/4114344/1033718
《nginx模块开发与架构解析》
复制代码
相关文章
相关标签/搜索