Nginx的负载均衡-加权轮询(WeightedRoundRobin)上篇Nginx版本:1.9.1
算法介绍
来看⼀个简单的Nginx负载均衡配置。
http {
upstream cluster {
server a weight=5;
server b weight=1;
server c weight=1;
}
server {
listen 80;
location / {
proxy_pass cluster;
}
}
}
当在upstream配置块中没有指定使⽤的负载均衡算法时,默认使⽤的是加权轮询。
按照上述配置,Nginx每收到7个客户端的请求,会把其中的5个转发给后端a,把其中的1个转发给后端b,
把其中的1个转发给后端c。
这就是所谓的加权轮询,看起来很简单,但是最早使⽤的加权轮询算法有个问题,就是7个请求对应的
后端序列是这样的:{ c, b, a, a, a, a, a },会有5个连续的请求落在后端a上,分布不太均匀。
⽬前使⽤的加权轮询叫做平滑的加权轮询(smooth weighted round-robin balancing),它和前者的区别是:
每7个请求对应的后端序列为 { a, a, b, a, c, a, a },转发给后端a的5个请求现在分散开来,不再是连续的。
摘录此算法的描述:
On each peer selection we increase current_weight of each eligible peer by its weight,
select peer with greatest current_weight and reduce its current_weight by total number
of weight points distributed among peers.
To preserve weight reduction in case of failures the effective_weight variable was introduced,
which usually matches peer's weight, but is reduced temoprarily on peer failures.[1]
每个后端peer都有三个权重变量,先解释下它们的含义。
(1) weight
配置⽂件中指定的该后端的权重,这个值是固定不变的。
(2) effective_weight
后端的有效权重,初始值为weight。
在释放后端时,如果发现和后端的通信过程中发⽣了错误,就减⼩effective_weight。
此后有新的请求过来时,在选取后端的过程中,再逐步增加effective_weight,最终⼜恢复到weight。
之所以增加这个字段,是为了当后端发⽣错误时,降低其权重。weight的所有形式
(3) current_weight
后端⽬前的权重,⼀开始为0,之后会动态调整。那么是怎么个动态调整呢?
每次选取后端时,会遍历集中所有后端,对于每个后端,让它的current_weight增加它的effective_weight,同时累加所有后端的effective_weight,保存为total。
如果该后端的current_weight是最⼤的,就选定这个后端,然后把它的current_weight减去total。
如果该后端没有被选定,那么current_weight不⽤减⼩。
弄清了三个weight字段的含义后,加权轮询算法可描述为:
1. 对于每个请求,遍历集中的所有可⽤后端,对于每个后端peer执⾏:
peer->current_weight += peer->effecitve_weight。
同时累加所有peer的effective_weight,保存为total。
2. 从集中选出current_weight最⼤的peer,作为本次选定的后端。
3. 对于本次选定的后端,执⾏:peer->current_weight -= total。
上述描述可能不太直观,来看个例⼦。
现在使⽤以下的upstream配置块:
upstream backend {
server a weight=4;
server b weight=2;
server c weight=1;
}
按照这个配置,每7个客户端请求中,a会被选中4次、b会被选中2次、c会被选中1次,且分布平滑。
我们来算算看是不是这样⼦的。
initial current_weight of a, b, c is { 0, 0, 0 }
通过上述过程,可得以下结论:
1. 7个请求中,a、b、c分别被选取了4、2、1次,符合它们的权重值。
2. 7个请求中,a、b、c被选取的顺序为a, b, a, c, a, b, a,分布均匀,权重⼤的后端a没有被连续选取。
3. 每经过7个请求后,a、b、c的current_weight⼜回到初始值{ 0, 0, 0 },因此上述流程是不断循环的。这个平滑的加权轮询算法背后应该有数学论证,这⾥就不继续研究了:)
本模块的数据结构
ngx_http_upstream_rr_peer_t
表⽰⼀台后端服务器。peer就是对端,指的是上游服务器端。
struct ngx_http_upstream_rr_peer_s {
struct sockaddr *sockaddr; /* 后端服务器的地址 */
socklen_t socklen; /* 地址的长度*/
ngx_str_t name; /* 后端服务器地址的字符串,server.addrs[i].name */
ngx_str_t server; /* server的名称,server.name */
ngx_int_t current_weight; /* 当前的权重,动态调整,初始值为0 */
ngx_int_t effective_weight; /* 有效的权重,会因为失败⽽降低 */
ngx_int_t weight; /* 配置项指定的权重,固定值 */
ngx_uint_t conns; /* 当前连接数 */
ngx_uint_t fails; /* "⼀段时间内",已经失败的次数 */
time_t accessed; /* 最近⼀次失败的时间点 */
time_t checked; /* ⽤于检查是否超过了"⼀段时间" */
ngx_uint_t max_fails; /* "⼀段时间内",最⼤的失败次数,固定值 */
time_t fail_timeout; /* "⼀段时间"的值,固定值 */
ngx_uint_t down; /* 服务器永久不可⽤的标志 */
...
ngx_http_upstream_rr_peer_t *next; /* 指向下⼀个后端,⽤于构成链表 */
.
..
} ngx_http_upstream_rr_peer_t;
ngx_http_upstream_rr_peers_t
表⽰⼀组后端服务器,⽐如⼀个后端集。
struct ngx_http_upstream_rr_peers_s {
ngx_uint_t number; /* 后端服务器的数量 */
...
ngx_uint_t total_weight; /* 所有后端服务器权重的累加值 */
unsigned single:1; /* 是否只有⼀台后端服务器 */
unsigned weighted:1; /* 是否使⽤权重 */
ngx_str_t *name; /* upstream配置块的名称 */
ngx_http_upstream_rr_peers_t *next; /* backup服务器集 */
ngx_http_upstream_rr_peer_t *peer; /* 后端服务器组成的链表 */
};
ngx_http_upstream_rr_peer_data_t
保存每个请求的负载均衡数据。
typedef struct {
ngx_http_upstream_rr_peers_t *peers; /* 后端集 */
ngx_http_upstream_rr_peer_t *current; /* 当前使⽤的后端服务器 */
uintptr_t *tried; /* 指向后端服务器的位图 */
uintptr_t data; /* 当后端服务器的数量较少时,⽤于存放其位图 */
} ngx_http_upstream_rr_peer_data_t;
通⽤的数据结构
以下是所有负载均衡模块都会使⽤到的⼀些数据结构。
ngx_http_upstream_server_t
表⽰upstream配置块中的⼀条server指令。
typedef struct {
ngx_str_t name; /* 服务器的名称 */
ngx_addr_t *addrs; /* 服务器地址的数组,因为同⼀个域名可能解析为多个IP */
ngx_uint_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;
server指令
Syntax: server address [parameters];
Context: upstream
Defines the address and other parameters of a server.
The address can be specified as domain name or IP address, with an optional port, or... If a port is not specified, the port 80 is used. A domain name that resolves to serveral IP addresses defines multiple servers at once.
server指令⽀持如下参数
weight = number
sets the weight of the server, by default 1.
max_fails = number
By default, the number of unsuccessful attempts is set to 1.
The zero value disables the accounting of attempts.
fail_timout = number
By default it is set to 10 seconds.
backup
marks the server as a backup server.
down
marks the server as permanently unavailable.
ngx_peer_connection_t
表⽰本机和后端的连接,也叫主动连接,⽤于upstream机制。

版权声明:本站内容均来自互联网,仅供演示用,请勿用于商业和其他非法用途。如果侵犯了您的权益请与我们联系QQ:729038198,我们将在24小时内删除。