sticky load balancing: a core feature or

Niklas Norberg niklas.norberg at bahnhof.se
Wed Apr 14 19:48:24 CEST 2010


You're welcome, here it is.

If you find it useful or find anything else please send me feedback.

Actually I figured out that it would have been best performance wise to
only set the backends i.e. look if there is a LB cookie and... when it's
necessary that is in vcl_fetch, vcl_pass, vcl_pipe and so on. But I'm
lasy and take this performance penalty to be sure to cover all cases
without digging to much, at least at this point.

So maybe the call:

	call recv_loadBalancingOnStickyCookie;

should have been best placed in vcl_prefetch but that sub was removed in
2.1.0 :(

One can also hard code some C string lengths and so on...


Regards

Niklas




ons 2010-04-14 klockan 10:32 -0700 skrev Rob Rogers:
> thanks,
> 
> would you mind sending or posting the entire LBSubs.vcl as a file?
> 
> thank you.
> 
> rob
> On Apr 14, 2010, at 10:27 AM, Niklas Norberg wrote:
> 
> > 
> > ons 2010-04-14 klockan 10:10 -0700 skrev Rob Rogers:
> > 
> > 
> > 
> >> 
> >> finally, per adding this config to MY config. how do you do that? That
> >> is, i don't want to just copy and paste this vcl snippet into my main
> >> varnish.vcl. Is there a way to include vcls from vcls?
> >> 
> >> 
> > 
> > Include VCL-snips with:
> > 
> > include "/etc/varnish/c.vcl";
> > include "/etc/varnish/subs.vcl";
> > include "/etc/varnish/defaultBackend.vcl";
> > include "/etc/varnish/backends.vcl";
> > include "/etc/varnish/LBsubs.vcl";
> > 
> > Best Regards
> > 
> > Niklas Norberg
> > 
> > 
> > 
> 
-------------- next part --------------

C{
    // Obs This string is also used hard coded in pure VCL code
    static const char VARNISH_LB_COOKIE_NAME[] = "VARNISH_LB=";
    // Let STICKY balance everything(/) for four hours a time:
    static const char VARNISH_LB_ENDING[] =
                "; path=/; Max-Age=14400; Comment=Varnish Sticky Load Balancing Cookie";
    static const int CANDIDATE_CNT = 3;
    static const int MAX_LEN_LB_ID = 2; // covers 1-99
    
    // Load balancing weights:
    // The first value should be the sum of the others.
    static const int LB_FACTOR[4] = {10, 7, 1, 2};
    // The first is not used, just to keep indexes same in the arrays.
    static int lbStatus[4] = {-1, 0, 0, 0};
    
    static int lastCandidate = 1;
    
    /**
        Load Balancing according to Request Counting Algorithm, see:
        http://httpd.apache.org/docs/2.2/mod/mod_proxy_balancer.html#requests
        or write your own.
    */
    int getCandidate4LB() {
        /*
        for each worker in workers
            worker lbstatus += worker lbfactor
            total factor    += worker lbfactor
            if worker lbstatus > candidate lbstatus
                candidate = worker
        candidate lbstatus -= total factor
        */
        int worker=1;
        // This local copy is really overkill
        int candidate = lastCandidate;
        while ( worker<=CANDIDATE_CNT ) {
            lbStatus[worker] += LB_FACTOR[worker];
            if ( lbStatus[worker] > lbStatus[candidate] )
                candidate = worker;
            worker++;
        }
        //int totalFactor = LB_FACTOR[0];
        //lbStatus[candidate] -= totalFactor;
        lbStatus[candidate] -= LB_FACTOR[0];
        lastCandidate = candidate;
        return candidate;
    }

    
}C

/**
    Check if a LB cookie is present.

    If it is
        - Copy the LB cookie value to a marker.
        - set backend
        - unset marker

    If it isn't
        - find candidate for LB and set value to marker
        - set backend
        - don't unset marker so a cookie can be set in vcl_deliver.
*/
sub recv_loadBalancingOnStickyCookie {
    if (req.http.Cookie ~ "VARNISH_LB=") {
        set req.http.StickyVarnish =
            regsub( req.http.Cookie, "^.*?VARNISH_LB=([^;]*);*.*$", "\1" );
        call chooseBackend;
        unset req.http.StickyVarnish;
    } else {
        call findCandidate4LB;
        call chooseBackend;
    }
}

/**
    Get candidate from some fancy smanchy algorithm.
    Set value to marker.
*/
sub findCandidate4LB {
    // set req.http.StickyVarnish = "X";:
    C{
    // get index for backend
    int idxBackend = getCandidate4LB();
    
    // Store index in a header marker so we can use this later
    char strBackendIndex[MAX_LEN_LB_ID + 1];
    sprintf(strBackendIndex, "%d", idxBackend);
    VRT_SetHdr(sp, HDR_REQ, "\016StickyVarnish:", strBackendIndex, vrt_magic_string_end);
    }C
//     C{
//     //Debug test:
//     int i=1;
//     while(i<=100) {
//         syslog( LOG_INFO, "getCandidate4LB(): %d", getCandidate4LB() );
//         i++;
//     }
//     }C
}

/**
    Choose backend (via director) from the marker ("StickyVarnish").
    Change this as necessary.
    Directors and Backends are defined elsewhere.
*/
sub chooseBackend {
    // if else in weight order, switch statements would have been better
    if (req.http.StickyVarnish == "1") {
        set req.backend = Backends1;
    } else
    if (req.http.StickyVarnish == "3") {
        set req.backend = Backends3;
    } else
    if (req.http.StickyVarnish == "2") {
        set req.backend = Backends2;
    }
}


/**
    Set the Sticky Varnish Load Balancing Cookie as:
    "VARNISH_LB=X; ..."
*/
sub deliver_setLBCookie {
    if (req.http.StickyVarnish) {
        C{
        
        // Also send cookie from backend if any
        char* existing_set_cookie = VRT_GetHdr(sp, HDR_OBJ, "\013Set-Cookie:");
        
        int len;
        if (existing_set_cookie == NULL)
            len = 0;
        else
            len = strlen(existing_set_cookie) + 1; // + 1 for "\r"
        len += strlen(VARNISH_LB_COOKIE_NAME);
        len += MAX_LEN_LB_ID;
        len += strlen(VARNISH_LB_ENDING);
        
        char set_cookie[len + 1];
        set_cookie[0] = '\0';
        
        if (existing_set_cookie != 0) {
            strcat( set_cookie, existing_set_cookie );
            strcat( set_cookie, "\r" );
        }
        strcat( set_cookie, VARNISH_LB_COOKIE_NAME );
        char* strBackend = VRT_GetHdr(sp, HDR_REQ, "\016StickyVarnish:");
        strcat( set_cookie, strBackend );
        strcat( set_cookie, VARNISH_LB_ENDING );
        
        // Send cookie(s)
        VRT_SetHdr(sp, HDR_RESP, "\013Set-Cookie:", set_cookie, vrt_magic_string_end);
        }C
    }
}


/**
    Call the LB subs from the subs:
    - vcl_recv
    - vcl_deliver
*/
/*
sub vcl_recv {
    call recv_loadBalancingOnStickyCookie;
}
sub vcl_deliver {
    call deliver_setLBCookie;
}
*/

/**
    If directors are defined as below we can in practice:
    Load balance to a specific backend by choosing a director
    and still have a fallback function.
    Risk for wrong backend = (1+1)/4294967295 = 5 * 10^-10,
    a price I'm willing to take for this fallback function.
    This way I can restart the backends and still keep the
    load balancing intact when all is up again.
*/
/*
director Backends1 random {
    { .backend = www1; .weight = 4294967295;}
    { .backend = www2; .weight = 1;}
    { .backend = www3; .weight = 1;}
}

director Backends2 random {
    { .backend = www1; .weight = 1;}
    { .backend = www2; .weight = 4294967295;}
    { .backend = www3; .weight = 1;}
}

director Backends3 random {
    { .backend = www1; .weight = 1;}
    { .backend = www2; .weight = 1;}
    { .backend = www3; .weight = 4294967295;}
}
*/


More information about the varnish-misc mailing list