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, "
"
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