diff --git a/CHANGES b/CHANGES index b7c892bac5..e63bae447e 100644 --- a/CHANGES +++ b/CHANGES @@ -2,6 +2,9 @@ Changes with Apache 2.1.3 [Remove entries to the current 2.0 section below, when backported] + *) proxy_balancer: Add in load-balancing via weighted traffic + byte count. [Jim Jagielski] + *) mod_disk_cache: Cache r->err_headers_out headers. This allows CGI scripts to be properly cached. [Justin Erenkrantz, Sander Striker] diff --git a/docs/manual/mod/mod_proxy.xml b/docs/manual/mod/mod_proxy.xml index 3f0249464b..34b3d6fcbf 100644 --- a/docs/manual/mod/mod_proxy.xml +++ b/docs/manual/mod/mod_proxy.xml @@ -443,8 +443,8 @@ loadfactor 1 Worker load factor. Used with BalancerMember. - It is a number between 1 and 100 and defines the load applied to - the worker. + It is a number between 1 and 100 and defines the normalized weighted + load applied to the worker. route - @@ -472,6 +472,13 @@ Parameter Default Description + lbmethod + - + Balancer load-balance method. Select the load-balancing scheduler + method to use. Either requests, to perform weighted + request counting or traffic, to perform weighted + traffic byte count balancing. Default is requests. + stickysession - Balancer sticky session name. The value is usually set to something diff --git a/modules/proxy/mod_proxy.c b/modules/proxy/mod_proxy.c index 83e87b9880..15e3ff5eed 100644 --- a/modules/proxy/mod_proxy.c +++ b/modules/proxy/mod_proxy.c @@ -253,6 +253,15 @@ static const char *set_balancer_param(apr_pool_t *p, balancer->max_attempts = ival; balancer->max_attempts_set = 1; } + else if (!strcasecmp(key, "lbmethod")) { + /* Which LB scheduler method */ + if (!strcasecmp(val, "traffic")) + balancer->lbmethod = lbmethod_traffic; + else if (!strcasecmp(val, "requests")) + balancer->lbmethod = lbmethod_requests; + else + return "lbmethod must be Traffic|Requests"; + } else { return "unknown Balancer parameter"; } @@ -1695,11 +1704,15 @@ static int proxy_status_hook(request_rec *r, int flags) ap_rputs("
\n

Proxy LoadBalancer Status for ", r); ap_rvputs(r, balancer->name, "

\n\n", NULL); ap_rputs("\n\n" - "" + "" "\n", r); ap_rvputs(r, "\n", + ap_rprintf(r, "", apr_time_sec(balancer->timeout)); + ap_rprintf(r, "\n", + balancer->lbmethod == lbmethod_requests ? "Requests" : + balancer->lbmethod == lbmethod_traffic ? "Traffic" : + "Unknown"); ap_rputs("
SSesTimeoutSSesTimeoutMethod
", balancer->sticky, NULL); - ap_rprintf(r, "%" APR_TIME_T_FMT "%" APR_TIME_T_FMT "%s
\n", r); ap_rputs("\n\n" "" diff --git a/modules/proxy/mod_proxy.h b/modules/proxy/mod_proxy.h index db403d104f..3a83c5ad8d 100644 --- a/modules/proxy/mod_proxy.h +++ b/modules/proxy/mod_proxy.h @@ -292,6 +292,10 @@ struct proxy_balancer { apr_interval_time_t timeout; /* Timeout for waiting on free connection */ int max_attempts; /* Number of attempts before failing */ char max_attempts_set; + enum { + lbmethod_requests = 1, + lbmethod_traffic = 2 + } lbmethod; /* XXX: Perhaps we will need the proc mutex too. * Altrough we are only using arithmetic operations diff --git a/modules/proxy/proxy_balancer.c b/modules/proxy/proxy_balancer.c index f0ea37d5c9..96774b8b01 100644 --- a/modules/proxy/proxy_balancer.c +++ b/modules/proxy/proxy_balancer.c @@ -161,7 +161,7 @@ static proxy_worker *find_session_route(proxy_balancer *balancer, } /* - * The idea behind this scheduler is the following: + * The idea behind the find_best_byrequests scheduler is the following: * * lbfactor is "how much we expect this worker to work", or "the worker's * normalized work quota". @@ -205,7 +205,7 @@ static proxy_worker *find_session_route(proxy_balancer *balancer, * b a d c d a c d b d ... * */ -static proxy_worker *find_best_worker(proxy_balancer *balancer, +static proxy_worker *find_best_byrequests(proxy_balancer *balancer, request_rec *r) { int i; @@ -213,9 +213,6 @@ static proxy_worker *find_best_worker(proxy_balancer *balancer, proxy_worker *worker = (proxy_worker *)balancer->workers->elts; proxy_worker *candidate = NULL; - if (PROXY_THREAD_LOCK(balancer) != APR_SUCCESS) - return NULL; - /* First try to see if we have available candidate */ for (i = 0; i < balancer->workers->nelts; i++) { /* If the worker is in error state run @@ -241,11 +238,88 @@ static proxy_worker *find_best_worker(proxy_balancer *balancer, if (candidate) { candidate->s->lbstatus -= total_factor; candidate->s->elected++; - PROXY_THREAD_UNLOCK(balancer); - return candidate; } - else { + + return candidate; +} + +/* + * The idea behind the find_best_bytraffic scheduler is the following: + * + * We know the amount of traffic (bytes in and out) handled by each + * worker. We normalize that traffic by each workers' weight. So assuming + * a setup as below: + * + * worker a b c + * lbfactor 1 1 3 + * + * the scheduler will allow worker c to handle 3 times the + * traffic of a and b. If each request/response results in the + * same amount of traffic, then c would be accessed 3 times as + * often as a or b. If, for example, a handled a request that + * resulted in a large i/o bytecount, then b and c would be + * chosen more often, to even things out. + */ +static proxy_worker *find_best_bytraffic(proxy_balancer *balancer, + request_rec *r) +{ + int i; + apr_off_t mytraffic = 0; + apr_off_t curmin = 0; + proxy_worker *worker = (proxy_worker *)balancer->workers->elts; + proxy_worker *candidate = NULL; + + /* First try to see if we have available candidate */ + for (i = 0; i < balancer->workers->nelts; i++) { + /* 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)) + ap_proxy_retry_worker("BALANCER", worker, r->server); + /* Take into calculation only the workers that are + * not in error state or not disabled. + */ + if (PROXY_WORKER_IS_USABLE(worker)) { + mytraffic = (worker->s->transferred/worker->s->lbfactor) + + (worker->s->read/worker->s->lbfactor); + if (!candidate || mytraffic < curmin) { + candidate = worker; + curmin = mytraffic; + } + } + worker++; + } + + if (candidate) { + candidate->s->elected++; + } + + return candidate; +} + +static proxy_worker *find_best_worker(proxy_balancer *balancer, + request_rec *r) +{ + proxy_worker *candidate = NULL; + + if (PROXY_THREAD_LOCK(balancer) != APR_SUCCESS) + return NULL; + + if (balancer->lbmethod == lbmethod_requests) { + candidate = find_best_byrequests(balancer, r); + } else if (balancer->lbmethod == lbmethod_traffic) { + candidate = find_best_bytraffic(balancer, r); + } else { PROXY_THREAD_UNLOCK(balancer); + return NULL; + } + + PROXY_THREAD_UNLOCK(balancer); + + if (candidate == NULL) { /* All the workers are in error state or disabled. * If the balancer has a timeout sleep for a while * and try again to find the worker. The chances are @@ -522,6 +596,21 @@ static int balancer_handler(request_rec *r) bsel->max_attempts = ival; bsel->max_attempts_set = 1; } + if ((val = apr_table_get(params, "lm"))) { + int ival = atoi(val); + switch(ival) { + case 0: + break; + case lbmethod_traffic: + bsel->lbmethod = lbmethod_traffic; + break; + case lbmethod_requests: + bsel->lbmethod = lbmethod_requests; + break; + default: + break; + } + } } if (wsel) { const char *val; @@ -599,12 +688,15 @@ static int balancer_handler(request_rec *r) "\">", NULL); ap_rvputs(r, balancer->name, "\n\n", NULL); ap_rputs("\n\n
SchHostStat
" - "" + "" "\n", r); ap_rvputs(r, "", apr_time_sec(balancer->timeout)); ap_rprintf(r, "\n", balancer->max_attempts); + ap_rprintf(r, "\n", + balancer->lbmethod == lbmethod_requests ? "Requests" : + balancer->lbmethod == lbmethod_traffic ? "Traffic" : "Unknown"); ap_rputs("
StickySesionTimeoutFailoverAttemptsStickySessionTimeoutFailoverAttemptsMethod
", balancer->sticky, NULL); ap_rprintf(r, "%" APR_TIME_T_FMT "%d%s
\n", r); ap_rputs("\n\n" "" @@ -681,6 +773,12 @@ static int balancer_handler(request_rec *r) ap_rputs("\n", bsel->max_attempts); + ap_rputs("\n", r); ap_rputs("\n", r); ap_rvputs(r, "
SchemeHost
Failover Attempts:
LB Method:
\nname + sizeof("balancer://") - 1, diff --git a/modules/proxy/proxy_util.c b/modules/proxy/proxy_util.c index 5c43fdee85..679cafd97d 100644 --- a/modules/proxy/proxy_util.c +++ b/modules/proxy/proxy_util.c @@ -1164,6 +1164,7 @@ PROXY_DECLARE(const char *) ap_proxy_add_balancer(proxy_balancer **balancer, memset(*balancer, 0, sizeof(proxy_balancer)); (*balancer)->name = uri; + (*balancer)->lbmethod = lbmethod_requests; (*balancer)->workers = apr_array_make(p, 5, sizeof(proxy_worker)); /* XXX Is this a right place to create mutex */ #if APR_HAS_THREADS