ngx_int_t
ngx_http_upstream_get_round_robin_peer( ngx_peer_connection_t *pc, void *data )
{
ngx_http_upstream_rr_peer_data_t *rrp = data;
time_t now;
uintptr_t m;
ngx_int_t rc;
ngx_uint_t i, n;
ngx_connection_t *c;
ngx_http_upstream_rr_peer_t *peer;
ngx_http_upstream_rr_peers_t *peers;
ngx_log_debug1( NGX_LOG_DEBUG_HTTP, pc->log, 0,
"get rr peer, try: %ui", pc->tries );
now = ngx_time();
/* ngx_lock_mutex(rrp->peers->mutex); */
//如果设定了 last_cached ,则last_cached递减1,设置pc的connection为cache的connection
//下标由last_cached指定,设定cached = 1 表示使用的是cache的连接
if ( rrp->peers->last_cached )
{
/* cached connection */
c = rrp->peers->cached[ rrp->peers->last_cached ];
rrp->peers->last_cached--;
/* ngx_unlock_mutex(ppr->peers->mutex); */
#if (NGX_THREADS)
c->read->lock = c->read->own_lock;
c->write->lock = c->write->own_lock;
#endif
pc->connection = c;
pc->cached = 1;
return NGX_OK;
}
//last_cached为0了,则
pc->cached = 0;
pc->connection = NULL;
if ( rrp->peers->single ) //只有一个backend,直接返回第0项
{
peer = &rrp->peers->peer[0];
}
else
{
/* there are several peers
多个backend,则第一次选根据weight来选出第一个,后面的round robin
第一个如果没有挂掉,则直接返回这项了,第二次来的时候还是根据weight来选;
如果根据weight来选,选到一个挂掉的节点,则tries减少1,设置为已经尝试过了,
然后再次根据weight来找,尝试i次,如果失败了就将fails清0,为下一轮筛选做准备。
这是tries已经减少1了,下次再来选择backend时,会到下面的函数,current递增1
指向下一个
*/
if ( pc->tries == rrp->peers->number )
{
/* it's a first try - get a current peer
第一次尝试,需要根据weight选出一个来作为开始
尝试 pc->tries 次来尝试获取,每次尝试一个backend, pc->tries 就减少1,
这里尝试pc->tries次,如果这个backend是ok的,则返回这个backend,
选中了一个不同的backend,则返回这个backend,如果多次选中了同一个backend
*/
//
i = pc->tries;
for ( ; ; )
{
//找到当前按照weight排序的最大的那个的下标
rrp->current = ngx_http_upstream_get_peer( rrp->peers );
ngx_log_debug2( NGX_LOG_DEBUG_HTTP, pc->log, 0,
"get rr peer, current: %ui %i",
rrp->current,
rrp->peers->peer[rrp->current].current_weight );
n = rrp->current / ( 8 * sizeof ( uintptr_t ) );
m = (uintptr_t) 1 << rrp->current % ( 8 * sizeof ( uintptr_t ) );
if ( !( rrp->tried[ n ] & m ) )
{
//还没有尝试过这个backend
peer = &rrp->peers->peer[ rrp->current ];
if ( !peer->down )
{
//如果该backend 失败的次数小于最大失败次数,则直接返回成功
if ( peer->max_fails == 0 || peer->fails < peer->max_fails )
{
break;
}
//当前失败的次数大于 max_fails 了,则看是否到了再次尝试的时间段,
//如果到了再次尝试的时间,则返回这个backend,再次尝试
if ( now - peer->accessed > peer->fail_timeout )
{
peer->fails = 0;
break;
}
// 还没有到再次尝试的时间段,则设置比重为0,不可能再次被选到了
peer->current_weight = 0;
}
else // 已经挂掉了,设置已经尝试过了
{
rrp->tried[ n ] |= m;
}
pc->tries--; // 尝试的backend 数量减少1
}
//已经尝试过这个backend了,递减i再次尝试一下
if ( pc->tries == 0 ) //尝试次数为0了,失败了
{
goto failed;
}
if ( --i == 0 ) // 尝试次数不是0,递减i看是不是0,如果为0则失败了
{
ngx_log_error( NGX_LOG_ALERT, pc->log, 0,
"round robin upstream stuck on %ui tries",
pc->tries );
goto failed;
}
}
//递减weight
peer->current_weight--;
}
else
{
//不是第一次尝试了, 尝试pc->tries次, round robin rrp->current 这个peer
i = pc->tries;
for ( ; ; )
{
n = rrp->current / ( 8 * sizeof ( uintptr_t ) );
m = (uintptr_t) 1 << rrp->current % ( 8 * sizeof ( uintptr_t ) );
if ( !( rrp->tried[ n ] & m ) ) //还没有尝试过
{
peer = &rrp->peers->peer[ rrp->current ];
if ( !peer->down )
{
if ( peer->max_fails == 0
|| peer->fails < peer->max_fails )
{
break;
}
if ( now - peer->accessed > peer->fail_timeout )
{
peer->fails = 0;
break;
}
peer->current_weight = 0;
}
else
{
rrp->tried[n] |= m;
}
pc->tries--;
}
//尝试过了,round robin 循环 递增指向下一个
rrp->current++;
if ( rrp->current >= rrp->peers->number )
{
rrp->current = 0;
}
if ( pc->tries == 0 ) //尝试次数为0了,则失败了
{
goto failed;
}
if ( --i == 0 )
{
ngx_log_error( NGX_LOG_ALERT, pc->log, 0,
"round robin upstream stuck on %ui tries",
pc->tries );
goto failed;
}
}
peer->current_weight--;
}
rrp->tried[ n ] |= m;
}
//此时的peer指向找到的那个backend
pc->sockaddr = peer->sockaddr;
pc->socklen = peer->socklen;
pc->name = &peer->name;
/* ngx_unlock_mutex(rrp->peers->mutex); */
if ( pc->tries == 1 && rrp->peers->next )
{
pc->tries += rrp->peers->next->number;
n = rrp->peers->next->number / ( 8 * sizeof ( uintptr_t ) ) + 1;
for ( i = 0; i < n; i++ )
{
rrp->tried[ i ] = 0;
}
}
return NGX_OK;
failed:
peers = rrp->peers;
if ( peers->next ) // 如果有backup的,则尝试backup备机
{
/* ngx_unlock_mutex(peers->mutex); */
ngx_log_debug0( NGX_LOG_DEBUG_HTTP, pc->log, 0, "backup servers" );
rrp->peers = peers->next;
pc->tries = rrp->peers->number;
n = rrp->peers->number / ( 8 * sizeof ( uintptr_t ) ) + 1;
for ( i = 0; i < n; i++ )
{
rrp->tried[ i ] = 0;
}
rc = ngx_http_upstream_get_round_robin_peer( pc, rrp );
if ( rc != NGX_BUSY )
{
return rc;
}
/* ngx_lock_mutex(peers->mutex); */
}
//如果没有backup,则将fails清0,再次尝试
/* all peers failed, mark them as live for quick recovery */
for ( i = 0; i < peers->number; i++ )
{
peers->peer[ i ].fails = 0;
}
/* ngx_unlock_mutex(peers->mutex); */
pc->name = peers->name;
return NGX_BUSY;
}
//按照weight来返回一个peer的下标
//找到当前比率最大的那项,返回这项的下标
static ngx_uint_t
ngx_http_upstream_get_peer( ngx_http_upstream_rr_peers_t *peers )
{
ngx_uint_t i, n;
ngx_http_upstream_rr_peer_t *peer;
peer = &peers->peer[0];
for ( ; ; )
{
for ( i = 0; i < peers->number; i++ )
{
if ( peer[i].current_weight <= 0 )
{
continue;
}
n = i;
while ( i < peers->number - 1 )
{
i++; // 和下一个比较是否大,如果比下一个小,则返回下一个
if ( peer[i].current_weight <= 0 )
{
continue;
}
if ( peer[n].current_weight * 1000 / peer[i].current_weight
> peer[n].weight * 1000 / peer[i].weight )
{
return n;
}
n = i;
}
//找到最后了,还没有比第i项比率大的,则说明这项就是,返回这项
if ( peer[i].current_weight > 0 )
{
n = i;
}
return n;
}
//走到这里的时候,说明都是0了,重新初始化为满额,接着循环找,总是能找到一个
for ( i = 0; i < peers->number; i++ )
{
peer[i].current_weight = peer[i].weight;
}
}
}
void
ngx_http_upstream_free_round_robin_peer(ngx_peer_connection_t *pc, void *data,
ngx_uint_t state)
{
ngx_http_upstream_rr_peer_data_t *rrp = data;
time_t now;
ngx_http_upstream_rr_peer_t *peer;
ngx_log_debug2(NGX_LOG_DEBUG_HTTP, pc->log, 0,
"free rr peer %ui %ui", pc->tries, state);
if ( state == 0 && pc->tries == 0 )
{
return;
}
/* TODO: NGX_PEER_KEEPALIVE */
if ( rrp->peers->single )
{
pc->tries = 0;
return;
}
if ( state & NGX_PEER_FAILED )
{
now = ngx_time();
peer = &rrp->peers->peer[ rrp->current ];
/* ngx_lock_mutex(rrp->peers->mutex); */
peer->fails++;
peer->accessed = now;
if ( peer->max_fails )
{
peer->current_weight -= peer->weight / peer->max_fails;
}
ngx_log_debug2( NGX_LOG_DEBUG_HTTP, pc->log, 0,
"free rr peer failed: %ui %i",
rrp->current, peer->current_weight );
if ( peer->current_weight < 0 )
{
peer->current_weight = 0;
}
/* ngx_unlock_mutex(rrp->peers->mutex); */
}
rrp->current++;
if ( rrp->current >= rrp->peers->number )
{
rrp->current = 0;
}
if ( pc->tries )
{
pc->tries--;
}
/* ngx_unlock_mutex(rrp->peers->mutex); */
}