Load Balancing

If you can't afford a commercial load balancing system and you don't mind managing load balancing from within your application, groundwork provides a rudimentary load balancing system that may suit your needs.

Groundwork's load balancing uses host-metric definition files and provides a loadbal class to interpret them. This approach is simple and inexpensive but does not provide advanced functionality such as detection and removal of hosts from the pool because they have gone down for some reason.

Host-metric definition files contain a list of hosts to distrubute load over and the relative power of each host. They have a .ldb extension, reside parallel to skin variable (.var) files and follow the same rules as .var files with regard to which one a given cgi uses.

Below is an example file with 5 hosts. The first column contains the hostname, the second column contains a metric value indicating the relative power of the machines. The first 2 hosts are equally powerful, the 3rd host is 2 times as powerful as either of the first 2 hosts, the 4th host is 3 times as powerful as the 3rd host and the last host is the least powerful; half the power of either of the first 2 hosts.

host1.firstworks.com 10
host2.firstworks.com 10
host3.firstworks.com 20
host4.firstworks.com 60
host5.firstworks.com 5

The nextHost() method of the loadbal class uses weighted-random selection to return a hostname from the list. nextHost() requires a seed value and returns the random number generated from the seed. This number can be used to seed future calls to nextHost() and can be passed on to the next cgi to seed its calls to nextHost() as well.

Below is some code illustrating the use of the loadbal and groundwork classes().

#include <groundwork/groundwork.h>
#include <groundwork/loadbal.h>
#include <groundwork/cgimodule.h>
#include <stdio.h>
#include <stdlib.h>
#include <time.h>

class sendpage : public groundwork, public loadbal {
        public:
                        sendpage(void *apistruct);
                int     execute();
};

sendpage::sendpage(void *apistruct) : groundwork(apistruct), loadbal(apistruct) {}

int sendpage::execute() {

        // read the .ldb file associated with this cgi
        initLoadbal(application(),skin(),module(),program());

        // If a seed was passed in from the previous page, use it, 
        // otherwise use the current time as the seed.
        char    *seedstr=formEntry("seed");
        long    seed;
        if (seedstr && seedstr[0]) {
                seed=atoi(seedstr);
        } else {
                seed=time(NULL);
        }

        // get the next host
        char    *nexthost=nextHost(seed,&seed);

        // save the seed as a string so we can pass it on
        char    nextseed[21];
        sprintf(nextseed,"%d",seed);

        // Parse the template file, replace all instances of "nexthost" with
        // the result of nextHost().  Pass the seed along too.
        sendHeader();
        return parseTemplate(NULL,templateFileDir(),templateFileName(),
                                NULL,
                                "nexthost",nexthost,
                                "seed",nextseed,
                                NULL);
}

MAIN {
        sendpage        sp(APISTRUCT);
        sp.execute();
}