nginx round robin 算法的实现



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); */
}

你可能感兴趣的:(c,UI,算法,nginx,cache,null)