return 0;
}
-/* Retrieve the parameter with the given name */
+/* Retrieve the parameter with the given name
+ * Something like 'JSESSIONID=12345...N'
+ */
static char *get_path_param(apr_pool_t *pool, char *url,
const char *name)
{
return NULL;
}
+/* Find the worker that has the 'route' defined
+ */
static proxy_runtime_worker *find_route_worker(proxy_balancer *balancer,
const char *route)
{
if (!*route)
*route = get_cookie_param(r, balancer->sticky);
if (*route) {
+ /* We have a route in path or in cookie
+ * Find the worker that has this route defined.
+ */
proxy_runtime_worker *worker = find_route_worker(balancer, *route);
- /* TODO: make worker status codes */
- /* See if we have a redirection route */
if (worker && !PROXY_WORKER_IS_USABLE(worker->w)) {
+ /* We have a worker that is unusable.
+ * It can be in error or disabled, but in case
+ * it has a redirection set use that redirection worker.
+ * This enables to safely remove the member from the
+ * balancer. Of course you will need a some kind of
+ * session replication between those two remote.
+ */
if (worker->w->redirect)
worker = find_route_worker(balancer, worker->w->redirect);
/* Check if the redirect worker is usable */
/* First try to see if we have available candidate */
for (i = 0; i < balancer->workers->nelts; i++) {
- /* See if the retry timeout is ellapsed
- * for the workers flagged as IN_ERROR
+ /* If the worker is in error state run
+ * retry on that worker. It will be marked as
+ * operational if the retry timeout is elapsed.
+ * The worker might still be unusable, but we try
+ * anyway.
*/
if (!PROXY_WORKER_IS_USABLE(worker->w))
ap_proxy_retry_worker("BALANCER", worker->w, r->server);
- /* If the worker is not in error state
- * or not disabled.
+ /* Take into calculation only the workers that are
+ * not in error state or not disabled.
*/
if (PROXY_WORKER_IS_USABLE(worker->w)) {
if (!candidate)
candidate = worker;
else {
- /* See if the worker has a larger number of free channels */
+ /* See if the worker has a larger number of free channels.
+ * This is used to favor the worker with the same balance
+ * factor and higher free channel number.
+ */
if (worker->w->cp->nfree > candidate->w->cp->nfree)
candidate = worker;
}
- /* Total factor should allways be 100.
- * This is for cases when worker is in error state.
- * It will force the even request distribution
+ /* Total factor is always 100 if all workers are
+ * operational.
+ * If we have a 60-20-20 load factors and the third goes down
+ * the effective factors for the rest two will be 70-30.
*/
total_factor += worker->s->lbfactor;
}
}
if (!candidate) {
/* All the workers are in error state or disabled.
- * If the balancer has a timeout wait.
+ * If the balancer has a timeout sleep for a while
+ * and try again to find the worker. The chances are
+ * that some other thread will release a connection.
+ * By default the timeout is not set, and the server
+ * returns SERVER_BUSY.
*/
#if APR_HAS_THREADS
if (balancer->timeout) {
*/
apr_interval_time_t timeout = balancer->timeout;
apr_interval_time_t step, tval = 0;
+ /* Set the timeout to 0 so that we don't
+ * end in infinite loop
+ */
balancer->timeout = 0;
step = timeout / 100;
while (tval < timeout) {
else {
/* We have at least one candidate that is not in
* error state or disabled.
- * Now calculate the appropriate one
+ * Now calculate the appropriate one.
+ */
+
+ /* Step one: Find the worker that has a lbfactor
+ * higher then a candidate found initially.
*/
worker = (proxy_runtime_worker *)balancer->workers->elts;
for (i = 0; i < balancer->workers->nelts; i++) {
- /* If the worker is not error state
- * or not in disabled mode
- */
if (PROXY_WORKER_IS_USABLE(worker->w)) {
- /* 1. Find the worker with higher lbstatus.
- * Lbstatus is of higher importance then
- * the number of empty slots.
- */
if (worker->s->lbstatus > candidate->s->lbstatus) {
candidate = worker;
}
}
worker++;
}
+ /* Step two: Recalculate the load statuses.
+ * Each usable worker load status is incremented
+ * by it's load factor.
+ */
worker = (proxy_runtime_worker *)balancer->workers->elts;
for (i = 0; i < balancer->workers->nelts; i++) {
- /* If the worker is not error state
- * or not in disabled mode
- */
if (PROXY_WORKER_IS_USABLE(worker->w)) {
- /* XXX: The lbfactor can be update using bytes transfered
- * Right now, use the round-robin scheme
+ /* Algo for w0(70%) w1(30%) tot(100):
+ * E1. Elected w0 -- highest lbstatus
+ * w0 += 70 -> w0 = 140 -- ovreflow set to 70
+ * w1 += 30 -> w1 = 60
+ * E2. Elected w0 -- highest lbstatus
+ * w0 += 70 -> w0 = 140 -- ovreflow set to 70
+ * w1 += 30 -> w1 = 90
+ * E3. Elected w1 -- highest lbstatus
+ * w0 += 70 -> w0 = 140 -- ovreflow set to 70
+ * w1 += 30 -> w1 = 120 -- ovreflow set to 30
+ * E4. Elected w0 -- highest lbstatus
+ * w0 += 70 -> w0 = 140 -- ovreflow set to 70
+ * w1 += 30 -> w1 = 60
+ * etc...
+ * Note:
+ * Although the load is 70-30, the actual load is
+ * 66-33, cause we can not have 2.33 : 1 requests,
+ * but rather 2:1 that is the closest integer number.
+ * In upper example the w0 will serve as twice as
+ * many requests as w1 does.
*/
worker->s->lbstatus += worker->s->lbfactor;
if (worker->s->lbstatus >= total_factor)
apr_status_t rv;
*worker = NULL;
- /* Step 1: check if the url is for us */
+ /* Step 1: check if the url is for us
+ * The url we can handle starts with 'balancer://'
+ */
if (!(*balancer = ap_proxy_get_balancer(r->pool, conf, *url)))
return DECLINED;
*worker = runtime->w;
}
/* Decrease the free channels number */
- /* XXX: This should be the function of apr_reslist */
+ /* XXX: This should be the function of apr_reslist
+ * There is a pending patch for apr_reslist that will
+ * enable to drop the need to maintain the free channels number
+ */
--(*worker)->cp->nfree;
PROXY_BALANCER_UNLOCK(*balancer);
+ /* Rewrite the url from 'balancer://url'
+ * to the 'worker_scheme://worker_hostname[:worker_port]/url'
+ * This replaces the balancers fictional name with the
+ * real hostname of the elected worker.
+ */
access_status = rewrite_url(r, *worker, url);
/* Add the session route to request notes if present */
if (route) {
}
/* increase the free channels number */
worker->cp->nfree++;
- /* TODO: calculate the bytes transfered */
-
- /* TODO: update the scoreboard status */
+ /* TODO: calculate the bytes transferred
+ * This will enable to elect the worker that has
+ * the lowest load.
+ * The bytes transferred depends on the protocol
+ * used, so each protocol handler should keep the
+ * track on that.
+ */
PROXY_BALANCER_UNLOCK(balancer);
ap_log_error(APLOG_MARK, APLOG_DEBUG, 0, r->server,
}
}
-/* Invoke handler */
+/* Manages the loadfactors and member status
+ */
static int balancer_handler(request_rec *r)
{
void *sconf = r->server->module_config;