nginx加权轮训算法
nginx加权轮训算法
nginx upstream负载均衡默认采用轮训方法
http {
upstream tomcats{
server 192.168.25.131:8080;
server 192.168.25.132:8080;
server 192.168.25.133:8080;
}
server {
listen 80;
server_name 192.168.25.130;
location / {
proxy_pass http://tomcats;
}
}
}
这里暂且将三台server服务器叫做a,b,c。浏览器访问192.168.25.130,每三次请求会均匀转发给a,b,c各一次。这是因为每台服务器默认的权重都是1,所以会均分请求。
现在为server加上权重,如下:
http {
upstream tomcats{
server 192.168.25.131:8080 weight=5;
server 192.168.25.132:8080 weight=2;
server 192.168.25.133:8080 weight=1;
}
server {
listen 80;
server_name 192.168.25.130;
location / {
proxy_pass http://tomcats;
}
}
}
这时再访问192.168.25.130,每8次请求中有5次转发给a,2次转发给b,1次转发给c。但8次请求并不是{a,a,a,a,a,b,b,c}这样转发的,连续的5次请求转发给a,是不均匀的。nginx使用的是平滑的加权轮训,这样装发给a的5次请求不再是连续的。
先看一下nginx中加权轮训算法实现
static ngx_http_upstream_rr_peer_t *
ngx_http_upstream_get_peer(ngx_http_upstream_rr_peer_data_t *rrp)
{
time_t now;
uintptr_t m;
ngx_int_t total;
ngx_uint_t i, n, p;
ngx_http_upstream_rr_peer_t *peer, *best;
now = ngx_time();
best = NULL;
total = 0;
#if (NGX_SUPPRESS_WARN)
p = 0;
#endif
for (peer = rrp->peers->peer, i = 0;
peer;
peer = peer->next, i++)
{
n = i / (8 * sizeof(uintptr_t));
m = (uintptr_t) 1 << i % (8 * sizeof(uintptr_t));
if (rrp->tried[n] & m) {
continue;
}
if (peer->down) {
continue;
}
if (peer->max_fails
&& peer->fails >= peer->max_fails
&& now - peer->checked <= peer->fail_timeout)
{
continue;
}
peer->current_weight += peer->effective_weight;
total += peer->effective_weight;
if (peer->effective_weight < peer->weight) {
peer->effective_weight++;
}
if (best == NULL || peer->current_weight > best->current_weight) {
best = peer;
p = i;
}
}
if (best == NULL) {
return NULL;
}
rrp->current = best;
n = p / (8 * sizeof(uintptr_t));
m = (uintptr_t) 1 << p % (8 * sizeof(uintptr_t));
rrp->tried[n] |= m;
best->current_weight -= total;
if (now - best->checked > best->fail_timeout) {
best->checked = now;
}
return best;
}
每个后端peer都有三个权重变量
- weight
配置文件中指定的该后端的权重,次值不变
- effective_weight
后端的有效权重,初始值为weight。
在释放后端时,如果发现和后端的通信过程中发生了错误,就减小effective_weight。
此后有新的请求过来时,在选取后端的过程中,再逐步增加effective_weight,最终又恢复到weight。
之所以增加这个字段,是为了当后端发生错误时,降低其权重。
- current_weight
后端目前的权重,一开始为0,之后会动态调整。那么是怎么个动态调整呢?
每次选取后端时,会遍历集群中所有后端,对于每个后端,让它的current_weight增加它的effective_weight,
同时累加所有后端的effective_weight,保存为total。
如果该后端的current_weight是最大的,就选定这个后端,然后把它的current_weight减去total。如果该后端没有被选定,那么current_weight不用减小。
.
对于每个请求,遍历集群中的所有可用后端,对于每个后端peer执行:
peer->current_weight += peer->effecitve_weight。
同时累加所有peer的effective_weight,保存为total。
从集群中选出current_weight最大的peer,作为本次选定的后端。
对于本次选定的后端,执行:peer->current_weight -= total。
upstream backend { server a weight=5; server b weight=2; server c weight=1; }
这样8次请求转发顺序是{a,b,a,a,c,a,b,a}