mod_proxy_balancer.c revision 53412cc2f0c1676b1df5cc65f54e1784e0dc42ca
181e56d8b348d301d615ccf5465ae600fee2867berikabele/* Copyright 1999-2005 The Apache Software Foundation or its licensors, as
181e56d8b348d301d615ccf5465ae600fee2867berikabele * applicable.
fd9abdda70912b99b24e3bf1a38f26fde908a74cnd * Licensed under the Apache License, Version 2.0 (the "License");
fd9abdda70912b99b24e3bf1a38f26fde908a74cnd * you may not use this file except in compliance with the License.
dd9f0e560e29dc86fba5f5d4fa5e72cda5cefb16slive * You may obtain a copy of the License at
96ad5d81ee4a2cc66a4ae19893efc8aa6d06fae7jailletc * Unless required by applicable law or agreed to in writing, software
7add1372edb1ee95a2c4d1314df4c7567bda7c62jim * distributed under the License is distributed on an "AS IS" BASIS,
7add1372edb1ee95a2c4d1314df4c7567bda7c62jim * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
d29d9ab4614ff992b0e8de6e2b88d52b6f1f153erbowen * See the License for the specific language governing permissions and
2e545ce2450a9953665f701bb05350f0d3f26275nd * limitations under the License.
7add1372edb1ee95a2c4d1314df4c7567bda7c62jim/* Load balancer module for Apache proxy */
7f5b59ccc63c0c0e3e678a168f09ee6a2f51f9d0ndstatic int proxy_balancer_canon(request_rec *r, char *url)
3b3b7fc78d1f5bfc2769903375050048ff41ff26nd const char *err;
2684d5de7d8996ac96df3a37e8f8a49c502f26dfjsl /* do syntatic check.
5a58787efeb02a1c3f06569d019ad81fd2efa06end * We break the URL into host, port, path, search
cfebc848e619d381e71d40b6f489db4aac180ee5rbowen err = ap_proxy_canon_netloc(r->pool, &url, NULL, NULL, &host, &port);
5a58787efeb02a1c3f06569d019ad81fd2efa06end "error parsing URL %s: %s",
7add1372edb1ee95a2c4d1314df4c7567bda7c62jim /* now parse path/search args, according to rfc1738 */
30471a4650391f57975f60bbb6e4a90be7b284bfhumbedooh /* N.B. if this isn't a true proxy request, then the URL _path_
7add1372edb1ee95a2c4d1314df4c7567bda7c62jim * has already been decoded. True proxy requests have r->uri
5a58787efeb02a1c3f06569d019ad81fd2efa06end * == r->unparsed_uri, and no others have that property.
57d0156f7bbd9ea3a72342cf9912aba61d118702rbowen /* process path */
57d0156f7bbd9ea3a72342cf9912aba61d118702rbowen path = ap_proxy_canonenc(r->pool, url, strlen(url), enc_path, 0, r->proxyreq);
dd9f0e560e29dc86fba5f5d4fa5e72cda5cefb16slive r->filename = apr_pstrcat(r->pool, "proxy:balancer://", host,
181e56d8b348d301d615ccf5465ae600fee2867berikabele "/", path, (search) ? "?" : "", (search) ? search : "", NULL);
57d0156f7bbd9ea3a72342cf9912aba61d118702rbowenstatic int init_balancer_members(proxy_server_conf *conf, server_rec *s,
181e56d8b348d301d615ccf5465ae600fee2867berikabele ap_proxy_initialize_worker_share(conf, workers, s);
dd9f0e560e29dc86fba5f5d4fa5e72cda5cefb16slive if (!(workers->s->status & PROXY_WORKER_INITIALIZED)) {
181e56d8b348d301d615ccf5465ae600fee2867berikabele workers->s->status |= (workers->status | PROXY_WORKER_INITIALIZED);
cfebc848e619d381e71d40b6f489db4aac180ee5rbowen "proxy: BALANCER: initialized balancer member %d for "
2684d5de7d8996ac96df3a37e8f8a49c502f26dfjsl "min=%d max=%d smax=%d",
57d0156f7bbd9ea3a72342cf9912aba61d118702rbowen /* Set to the original configuration */
57d0156f7bbd9ea3a72342cf9912aba61d118702rbowen /* Set default number of attempts to the number of
57d0156f7bbd9ea3a72342cf9912aba61d118702rbowen if (!balancer->max_attempts_set && balancer->workers->nelts > 1) {
57d0156f7bbd9ea3a72342cf9912aba61d118702rbowen balancer->max_attempts = balancer->workers->nelts - 1;
57d0156f7bbd9ea3a72342cf9912aba61d118702rbowen/* Retrieve the parameter with the given name
57d0156f7bbd9ea3a72342cf9912aba61d118702rbowen * Something like 'JSESSIONID=12345...N'
57d0156f7bbd9ea3a72342cf9912aba61d118702rbowenstatic char *get_path_param(apr_pool_t *pool, char *url,
57d0156f7bbd9ea3a72342cf9912aba61d118702rbowen const char *name)
57d0156f7bbd9ea3a72342cf9912aba61d118702rbowen for (path = strstr(url, name); path; path = strstr(path + 1, name)) {
57d0156f7bbd9ea3a72342cf9912aba61d118702rbowen * Session path was found, get it's value
20189240503ef2c8f5dc6e2248b57faab4b23b5and *q = '\0';
57d0156f7bbd9ea3a72342cf9912aba61d118702rbowenstatic char *get_cookie_param(request_rec *r, const char *name)
b9f7b2acbe4a228c3eaeb6293554ca9488330c83rbowen const char *cookies;
b9f7b2acbe4a228c3eaeb6293554ca9488330c83rbowen if ((cookies = apr_table_get(r->headers_in, "Cookie"))) {
9a58dc6a2b26ec128b1270cf48810e705f1a90dbsf for (start_cookie = ap_strstr_c(cookies, name); start_cookie;
b9f7b2acbe4a228c3eaeb6293554ca9488330c83rbowen start_cookie = ap_strstr_c(start_cookie + 1, name)) {
b9f7b2acbe4a228c3eaeb6293554ca9488330c83rbowen * Session cookie was found, get it's value
57d0156f7bbd9ea3a72342cf9912aba61d118702rbowen/* Find the worker that has the 'route' defined
2684d5de7d8996ac96df3a37e8f8a49c502f26dfjslstatic proxy_worker *find_route_worker(proxy_balancer *balancer,
dd9f0e560e29dc86fba5f5d4fa5e72cda5cefb16slive const char *route)
181e56d8b348d301d615ccf5465ae600fee2867berikabele proxy_worker *worker = (proxy_worker *)balancer->workers->elts;
181e56d8b348d301d615ccf5465ae600fee2867berikabele if (*(worker->s->route) && strcmp(worker->s->route, route) == 0) {
57d0156f7bbd9ea3a72342cf9912aba61d118702rbowenstatic proxy_worker *find_session_route(proxy_balancer *balancer,
2684d5de7d8996ac96df3a37e8f8a49c502f26dfjsl /* Try to find the sticky route inside url */
2684d5de7d8996ac96df3a37e8f8a49c502f26dfjsl *route = get_path_param(r->pool, *url, balancer->sticky);
57d0156f7bbd9ea3a72342cf9912aba61d118702rbowen "proxy: BALANCER: Found value %s for "
57d0156f7bbd9ea3a72342cf9912aba61d118702rbowen * If we found a value for sticksession, find the first '.' within.
57d0156f7bbd9ea3a72342cf9912aba61d118702rbowen * Everything after '.' (if present) is our route.
57d0156f7bbd9ea3a72342cf9912aba61d118702rbowen if ((*route) && ((*route = strchr(*route, '.')) != NULL ))
dd9f0e560e29dc86fba5f5d4fa5e72cda5cefb16slive /* We have a route in path or in cookie
dd9f0e560e29dc86fba5f5d4fa5e72cda5cefb16slive * Find the worker that has this route defined.
57d0156f7bbd9ea3a72342cf9912aba61d118702rbowen /* We have a worker that is unusable.
57d0156f7bbd9ea3a72342cf9912aba61d118702rbowen * It can be in error or disabled, but in case
57d0156f7bbd9ea3a72342cf9912aba61d118702rbowen * it has a redirection set use that redirection worker.
57d0156f7bbd9ea3a72342cf9912aba61d118702rbowen * This enables to safely remove the member from the
57d0156f7bbd9ea3a72342cf9912aba61d118702rbowen * balancer. Of course you will need a some kind of
57d0156f7bbd9ea3a72342cf9912aba61d118702rbowen * session replication between those two remote.
57d0156f7bbd9ea3a72342cf9912aba61d118702rbowen worker = find_route_worker(balancer, worker->s->redirect);
dd9f0e560e29dc86fba5f5d4fa5e72cda5cefb16slive /* Check if the redirect worker is usable */
57d0156f7bbd9ea3a72342cf9912aba61d118702rbowenstatic proxy_worker *find_best_worker(proxy_balancer *balancer,
dd9f0e560e29dc86fba5f5d4fa5e72cda5cefb16slive PROXY_THREAD_UNLOCK(balancer);
cfebc848e619d381e71d40b6f489db4aac180ee5rbowen return NULL;
57d0156f7bbd9ea3a72342cf9912aba61d118702rbowen /* All the workers are in error state or disabled.
dd9f0e560e29dc86fba5f5d4fa5e72cda5cefb16slive * If the balancer has a timeout sleep for a while
57d0156f7bbd9ea3a72342cf9912aba61d118702rbowen * and try again to find the worker. The chances are
57d0156f7bbd9ea3a72342cf9912aba61d118702rbowen * that some other thread will release a connection.
57d0156f7bbd9ea3a72342cf9912aba61d118702rbowen * By default the timeout is not set, and the server
57d0156f7bbd9ea3a72342cf9912aba61d118702rbowen * returns SERVER_BUSY.
cfebc848e619d381e71d40b6f489db4aac180ee5rbowen /* XXX: This can perhaps be build using some
57d0156f7bbd9ea3a72342cf9912aba61d118702rbowen * smarter mechanism, like tread_cond.
cfebc848e619d381e71d40b6f489db4aac180ee5rbowen * But since the statuses can came from
57d0156f7bbd9ea3a72342cf9912aba61d118702rbowen * different childs, use the provided algo.
57d0156f7bbd9ea3a72342cf9912aba61d118702rbowen /* Set the timeout to 0 so that we don't
57d0156f7bbd9ea3a72342cf9912aba61d118702rbowen * end in infinite loop
57d0156f7bbd9ea3a72342cf9912aba61d118702rbowen /* Try again */
57d0156f7bbd9ea3a72342cf9912aba61d118702rbowen /* restore the timeout */
57d0156f7bbd9ea3a72342cf9912aba61d118702rbowenstatic int rewrite_url(request_rec *r, proxy_worker *worker,
57d0156f7bbd9ea3a72342cf9912aba61d118702rbowen /* we break the URL into host, port, uri */
f92d094f13138be62bcc1a4b8187d72705d2cb05jsl return ap_proxyerror(r, HTTP_BAD_REQUEST, apr_pstrcat(r->pool,
57d0156f7bbd9ea3a72342cf9912aba61d118702rbowen *url = apr_pstrcat(r->pool, worker->name, path, NULL);
f92d094f13138be62bcc1a4b8187d72705d2cb05jslstatic int proxy_balancer_pre_request(proxy_worker **worker,
57d0156f7bbd9ea3a72342cf9912aba61d118702rbowen /* Step 1: check if the url is for us
57d0156f7bbd9ea3a72342cf9912aba61d118702rbowen * The url we can handle starts with 'balancer://'
dd9f0e560e29dc86fba5f5d4fa5e72cda5cefb16slive * If balancer is already provided skip the search
cfebc848e619d381e71d40b6f489db4aac180ee5rbowen * for balancer, because this is failover attempt.
57d0156f7bbd9ea3a72342cf9912aba61d118702rbowen !(*balancer = ap_proxy_get_balancer(r->pool, conf, *url)))
57d0156f7bbd9ea3a72342cf9912aba61d118702rbowen /* Step 2: find the session route */
57d0156f7bbd9ea3a72342cf9912aba61d118702rbowen runtime = find_session_route(*balancer, r, &route, url);
57d0156f7bbd9ea3a72342cf9912aba61d118702rbowen /* Lock the LoadBalancer
57d0156f7bbd9ea3a72342cf9912aba61d118702rbowen * XXX: perhaps we need the process lock here
57d0156f7bbd9ea3a72342cf9912aba61d118702rbowen if ((rv = PROXY_THREAD_LOCK(*balancer)) != APR_SUCCESS) {
57d0156f7bbd9ea3a72342cf9912aba61d118702rbowen "proxy: BALANCER: lock");
57d0156f7bbd9ea3a72342cf9912aba61d118702rbowen /* We have a sticky load balancer
57d0156f7bbd9ea3a72342cf9912aba61d118702rbowen * Update the workers status
70fce89e67f707c03f70d437a64e189a205ffc35jsl * so that even session routes get
f92d094f13138be62bcc1a4b8187d72705d2cb05jsl * into account.
57d0156f7bbd9ea3a72342cf9912aba61d118702rbowen /* Take into calculation only the workers that are
57d0156f7bbd9ea3a72342cf9912aba61d118702rbowen * not in error state or not disabled.
f92d094f13138be62bcc1a4b8187d72705d2cb05jsl "proxy: BALANCER: (%s). All workers are in error state for route (%s)",
57d0156f7bbd9ea3a72342cf9912aba61d118702rbowen "proxy: BALANCER: (%s). All workers are in error state",
57d0156f7bbd9ea3a72342cf9912aba61d118702rbowen /* Rewrite the url from 'balancer://url'
57d0156f7bbd9ea3a72342cf9912aba61d118702rbowen * to the 'worker_scheme://worker_hostname[:worker_port]/url'
57d0156f7bbd9ea3a72342cf9912aba61d118702rbowen * This replaces the balancers fictional name with the
57d0156f7bbd9ea3a72342cf9912aba61d118702rbowen * real hostname of the elected worker.
57d0156f7bbd9ea3a72342cf9912aba61d118702rbowen /* Add the session route to request notes if present */
57d0156f7bbd9ea3a72342cf9912aba61d118702rbowen apr_table_setn(r->notes, "session-sticky", (*balancer)->sticky);
70fce89e67f707c03f70d437a64e189a205ffc35jsl "proxy: BALANCER (%s) worker (%s) rewritten to %s",
57d0156f7bbd9ea3a72342cf9912aba61d118702rbowenstatic int proxy_balancer_post_request(proxy_worker *worker,
57d0156f7bbd9ea3a72342cf9912aba61d118702rbowen "proxy: BALANCER: lock");
57d0156f7bbd9ea3a72342cf9912aba61d118702rbowen /* TODO: calculate the bytes transferred
57d0156f7bbd9ea3a72342cf9912aba61d118702rbowen * This will enable to elect the worker that has
57d0156f7bbd9ea3a72342cf9912aba61d118702rbowen * the lowest load.
70fce89e67f707c03f70d437a64e189a205ffc35jsl * The bytes transferred depends on the protocol
70fce89e67f707c03f70d437a64e189a205ffc35jsl * used, so each protocol handler should keep the
70fce89e67f707c03f70d437a64e189a205ffc35jsl * track on that.
70fce89e67f707c03f70d437a64e189a205ffc35jsl "proxy_balancer_post_request for (%s)", balancer->name);
181e56d8b348d301d615ccf5465ae600fee2867berikabelestatic void recalc_factors(proxy_balancer *balancer)
dd9f0e560e29dc86fba5f5d4fa5e72cda5cefb16slive /* Recalculate lbfactors */
181e56d8b348d301d615ccf5465ae600fee2867berikabele workers = (proxy_worker *)balancer->workers->elts;
cdf912a26be170e1528d1f6c9db8a2a8f9937420pcs /* Special case if there is only one worker it's
181e56d8b348d301d615ccf5465ae600fee2867berikabele * load factor will always be 1
181e56d8b348d301d615ccf5465ae600fee2867berikabele /* Update the status entries */
57d0156f7bbd9ea3a72342cf9912aba61d118702rbowen/* Manages the loadfactors and member status
2df40fa998d3364133c4dd29eb395f5ae70dfc1fslive const char *name;
103f414dc9a63f76c48bc1d81162ea6f3c5ef495slive /* is this for us? */
2df40fa998d3364133c4dd29eb395f5ae70dfc1fslive * Special case: workers are allowed path information
103f414dc9a63f76c48bc1d81162ea6f3c5ef495slive if (strcmp(args, "w") || (access_status != HTTP_NOT_FOUND))
5d81a8bafa2703e966db5bfd5abb33b176d47589nd /* First set the params */
5d81a8bafa2703e966db5bfd5abb33b176d47589nd const char *val;
57d0156f7bbd9ea3a72342cf9912aba61d118702rbowen const char *val;
57d0156f7bbd9ea3a72342cf9912aba61d118702rbowen if (strlen(val) && strlen(val) < PROXY_WORKER_MAX_ROUTE_SIZ)
57d0156f7bbd9ea3a72342cf9912aba61d118702rbowen if (strlen(val) && strlen(val) < PROXY_WORKER_MAX_ROUTE_SIZ)
57d0156f7bbd9ea3a72342cf9912aba61d118702rbowen ap_rputs("<?xml version=\"1.0\" encoding=\"UTF-8\" ?>\n", r);
57d0156f7bbd9ea3a72342cf9912aba61d118702rbowen ap_rputs("<httpd:manager xmlns:httpd=\"http://httpd.apache.org\">\n", r);
181e56d8b348d301d615ccf5465ae600fee2867berikabele balancer = (proxy_balancer *)conf->balancers->elts;
57d0156f7bbd9ea3a72342cf9912aba61d118702rbowen ap_rvputs(r, " <httpd:name>", balancer->name, "</httpd:name>\n", NULL);
57d0156f7bbd9ea3a72342cf9912aba61d118702rbowen ap_rprintf(r, " <httpd:loadfactor>%d</httpd:loadfactor>\n",
57d0156f7bbd9ea3a72342cf9912aba61d118702rbowen "<html><head><title>Balancer Manager</title></head>\n", r);
181e56d8b348d301d615ccf5465ae600fee2867berikabele ap_rvputs(r, ap_get_server_name(r), "</h1>\n\n", NULL);
181e56d8b348d301d615ccf5465ae600fee2867berikabele ap_rputs("<hr />\n<h3>LoadBalancer Status for ", r);
57d0156f7bbd9ea3a72342cf9912aba61d118702rbowen ap_rputs("\n\n<table border=\"0\" style=\"text-align: left;\"><tr>"
57d0156f7bbd9ea3a72342cf9912aba61d118702rbowen "<th>StickySession</th><th>Timeout</th><th>FailoverAttempts</th><th>Method</th>"
57d0156f7bbd9ea3a72342cf9912aba61d118702rbowen "</tr>\n<tr>", r);
181e56d8b348d301d615ccf5465ae600fee2867berikabele ap_rprintf(r, "<td>%d</td>\n", balancer->max_attempts);
57d0156f7bbd9ea3a72342cf9912aba61d118702rbowen ap_rputs("\n\n<table border=\"0\" style=\"text-align: left;\"><tr>"
57d0156f7bbd9ea3a72342cf9912aba61d118702rbowen "<th>Worker URL</th>"
57d0156f7bbd9ea3a72342cf9912aba61d118702rbowen "<th>Route</th><th>RouteRedir</th>"
e884f58207082fa2136d5fc86635c31252338948erikabele "<th>Factor</th><th>Status</th>"
e884f58207082fa2136d5fc86635c31252338948erikabele "</tr>\n", r);
7add1372edb1ee95a2c4d1314df4c7567bda7c62jim ap_rprintf(r, "</td><td>%d</td><td>", worker->s->lbfactor);
57d0156f7bbd9ea3a72342cf9912aba61d118702rbowen else if (worker->s->status & PROXY_WORKER_INITIALIZED)
cfebc848e619d381e71d40b6f489db4aac180ee5rbowen ap_rputs("<table><tr><td>Load factor:</td><td><input name=\"lf\" type=text ", r);
221e8a19d2201546e7efe590924383c2a7f0fd72slive ap_rprintf(r, "value=\"%d\"></td><tr>\n", wsel->s->lbfactor);
221e8a19d2201546e7efe590924383c2a7f0fd72slive ap_rputs("<tr><td>Route:</td><td><input name=\"wr\" type=text ", r);
221e8a19d2201546e7efe590924383c2a7f0fd72slive ap_rputs("<tr><td>Route Redirect:</td><td><input name=\"rr\" type=text ", r);
3b3b7fc78d1f5bfc2769903375050048ff41ff26nd ap_rputs("<tr><td>Status:</td><td>Disabled: <input name=\"dw\" value=\"Disable\" type=radio", r);
7f5b59ccc63c0c0e3e678a168f09ee6a2f51f9d0nd ap_rputs("> | Enabled: <input name=\"dw\" value=\"Enable\" type=radio", r);
0d0ba3a410038e179b695446bb149cce6264e0abnd ap_rputs("<tr><td colspan=2><input type=submit value=\"Submit\"></td></tr>\n", r);
727872d18412fc021f03969b8641810d8896820bhumbedooh ap_rvputs(r, "</table>\n<input type=hidden name=\"w\" ", NULL);
cc7e1025de9ac63bd4db6fe7f71c158b2cf09fe4humbedooh ap_rvputs(r, "value=\"", ap_escape_uri(r->pool, wsel->name), "\">\n", NULL);
cc7e1025de9ac63bd4db6fe7f71c158b2cf09fe4humbedooh ap_rvputs(r, "value=\"", bsel->name + sizeof("balancer://") - 1,
0d0ba3a410038e179b695446bb149cce6264e0abnd else if (bsel) {
727872d18412fc021f03969b8641810d8896820bhumbedooh ap_rputs("<table><tr><td>StickySession Identifier:</td><td><input name=\"ss\" type=text ", r);
30471a4650391f57975f60bbb6e4a90be7b284bfhumbedooh ap_rputs("></td><tr>\n<tr><td>Timeout:</td><td><input name=\"tm\" type=text ", r);
205f749042ed530040a4f0080dbcb47ceae8a374rjung ap_rprintf(r, "value=\"%" APR_TIME_T_FMT "\"></td></tr>\n",
0d0ba3a410038e179b695446bb149cce6264e0abnd ap_rputs("<tr><td>Failover Attempts:</td><td><input name=\"fa\" type=text ", r);
7fec19672a491661b2fe4b29f685bc7f4efa64d4nd ap_rputs("<tr><td>LB Method:</td><td><select name=\"lm\">", r);
method++;
return OK;
balancer++;
s = s->next;
request_rec *r)
int total_factor = 0;
worker++;
if (mycandidate) {
return mycandidate;
request_rec *r)
worker++;
if (mycandidate) {
return mycandidate;