mod_proxy_balancer.c revision 0cebbbe2b9b7f92eeadf2fbf83432e4a47f8d41c
458N/A/* Licensed to the Apache Software Foundation (ASF) under one or more 458N/A * contributor license agreements. See the NOTICE file distributed with 458N/A * this work for additional information regarding copyright ownership. 458N/A * The ASF licenses this file to You under the Apache License, Version 2.0 458N/A * (the "License"); you may not use this file except in compliance with 458N/A * the License. You may obtain a copy of the License at 458N/A * Unless required by applicable law or agreed to in writing, software 458N/A * distributed under the License is distributed on an "AS IS" BASIS, 458N/A * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 458N/A * See the License for the specific language governing permissions and 458N/A * limitations under the License. 458N/A/* Load balancer module for Apache proxy */ 2833N/A * Register our mutex type before the config is read so we 844N/A * can adjust the mutex settings using the Mutex directive. 458N/A /* TODO: offset of BALANCER_PREFIX ?? */ 458N/A "proxy: BALANCER: canonicalising URL %s",
url);
458N/A * We break the URL into host, port, path, search 646N/A "error parsing URL %s: %s",
646N/A * process the path. With proxy-noncanon set (by 646N/A * mod_proxy) we use the raw, unparsed uri 3497N/A /* Set default number of attempts to the number of 3497N/A/* Retrieve the parameter with the given name 3497N/A * Something like 'JSESSIONID=12345...N' 3497N/A * Session path was found, get its value 3747N/A * Session cookie was found, get its value 3817N/A/* Find the worker that has the 'route' defined * 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 * 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 some kind of * session replication between those two remote. /* Check if the redirect worker is usable */ * 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 /* Try to find the sticky route inside url */ "proxy: BALANCER: Found value %s for " "proxy: BALANCER: Found value %s for " * If we found a value for sticksession, find the first '.' within. * Everything after '.' (if present) is our route. "proxy: BALANCER: Found route %s", *
route);
/* We have a route in path or in cookie * Find the worker that has this route defined. * Notice that the route of the worker chosen is different from * the route supplied by the client. "proxy: BALANCER: Route changed from %s to %s",
"proxy: BALANCER: (%s). Lock failed for find_best_worker()",
balancer->
name);
"proxy: BALANCER: (%s). Unlock failed for find_best_worker()",
balancer->
name);
/* 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 * that some other thread will release a connection. * By default the timeout is not set, and the server /* XXX: This can perhaps be build using some * smarter mechanism, like tread_cond. * But since the statuses can came from * different childs, use the provided algo. /* Set the timeout to 0 so that we don't /* restore the timeout */ /* we break the URL into host, port, uri */ "missing worker. URI cannot be parsed: ", *
url,
/* Try if we can recover */ /* If all workers are in error state force the recovery. "proxy: BALANCER: (%s). Forcing recovery for worker (%s)",
/* Step 1: check if the url is for us * The url we can handle starts with 'balancer://' * If balancer is already provided skip the search * for balancer, because this is failover attempt. /* Step 2: Lock the LoadBalancer * XXX: perhaps we need the process lock here "proxy: BALANCER: (%s). Lock failed for pre_request",
/* Step 3: force recovery */ /* Step 3.5: Update member list for the balancer */ /* TODO: Implement as provider! */ /* Step 4: find the session route */ /* Call the LB implementation */ else {
/* Use the default one */ /* We have a sticky load balancer * Update the workers status * so that even session routes get /* Take into calculation only the workers that are * not in error state or not disabled. * We have a route provided that doesn't match the * balancer name. See if the provider route is the * member of the same balancer in which case return 503 "proxy: BALANCER: (%s). All workers are in error state for route (%s)",
"proxy: BALANCER: (%s). Unlock failed for pre_request",
"proxy: BALANCER: (%s). Unlock failed for pre_request",
"proxy: BALANCER: (%s). All workers are in error state",
"proxy: BALANCER: (%s). No workers in balancer",
* This balancer has sticky sessions and the client either has not * supplied any routing information or all workers for this route * including possible redirect and hotstandby workers are in error * state, but we have found another working worker for this * balancer where we can send the request. Thus notice that we have * changed the route to the backend. /* 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. /* Add the session route to request notes if present */ /* Add session info to env. */ "BALANCER_SESSION_STICKY",
sticky);
"BALANCER_SESSION_ROUTE",
route);
"proxy: BALANCER (%s) worker (%s) rewritten to %s",
"proxy: BALANCER: (%s). Lock failed for post_request",
"proxy: BALANCER: (%s). Forcing recovery for worker (%s), failonstatus %d",
"proxy: BALANCER: (%s). Unlock failed for post_request",
/* Recalculate lbfactors */ /* Special case if there is only one worker its * load factor will always be 1 /* Update the status entries */ /* balancer_post_config() will be called twice during startup. So, don't * set up the static data the 1st time through. */ * Go thru each Vhost and create the shared mem slotmem for * each balancer's workers /* Initialize shared scoreboard data */ /* no need for the 'balancer://' prefix */ /* Create global mutex */ /* setup shm for balancers */ /* create slotmem slots for workers */ /* sync all timestamps */ /* now go thru each worker */ /* Manages the loadfactors and member status * The balancer, worker and nonce are obtained from * the request args (?b=...&w=...&nonce=....). * All other params are pulled from any POST "proxy: BALANCER: (%s). Lock failed for balancer_handler",
"proxy: BALANCER: (%s). Unlock failed for balancer_handler",
/* Check that the supplied nonce matches this server's nonce; * otherwise ignore all parameters, to prevent a CSRF attack. */ /* First set the params */ /* if enabling, we need to reset all lb params */ if (
ival >= 0 &&
ival <=
7200) {
/* 2 hrs enuff? */ if (*
val ==
'-' && *(
val+
1) ==
'\0')
(*
val ==
'1' && *(
val+
1) ==
'\0') &&
"proxy: BALANCER: (%s). Lock failed for adding worker",
"proxy: BALANCER: (%s). Unlock failed for adding worker",
"proxy: BALANCER: (%s). Unlock failed for adding worker",
"proxy: BALANCER: (%s). Unlock failed for adding worker",
"proxy: BALANCER: (%s). Unlock failed for adding worker",
/* sync all timestamps */ /* by default, all new workers are disabled */ "proxy: BALANCER: (%s). Unlock failed for adding worker",
ap_rputs(
"<?xml version='1.0' encoding='UTF-8' ?>\n", r);
"</httpd:scheme>\n",
NULL);
"</httpd:hostname>\n",
NULL);
ap_rprintf(r,
" <httpd:loadfactor>%d</httpd:loadfactor>\n",
"<html><head><title>Balancer Manager</title></head>\n", r);
ap_rputs(
"<body><h1>Load Balancer Manager for ", r);
ap_rputs(
"<hr />\n<h3>LoadBalancer Status for ", r);
ap_rputs(
"\n\n<table border='0' style='text-align: left;'><tr>" "<th>MaxMembers</th><th>StickySession</th><th>DisableFailover</th><th>Timeout</th><th>FailoverAttempts</th><th>Method</th>" /* the below is a safe cast, since the number of slots total will * never be more than max_workers, which is restricted to int */ ap_rputs(
"<td align='center'> (None) ", r);
ap_rputs(
"\n\n<table border='0' style='text-align: left;'><tr>" "<th>Route</th><th>RouteRedir</th>" "<th>Factor</th><th>Set</th><th align='center'>Status</th>" "<th>Elected</th><th>Busy</th><th>Load</th><th>To</th><th>From</th>" ap_rputs(
"</td><td align='center'>", r);
ap_rputs(
"<h3>Edit worker settings for ", r);
ap_rputs(
"<dl>\n<table><tr><td>Load factor:</td><td><input name='w_lf' id='w_lf' type=text ", r);
ap_rputs(
"<tr><td>LB Set:</td><td><input name='w_ls' id='w_ls' type=text ", r);
ap_rputs(
"<tr><td>Route:</td><td><input name='w_wr' id='w_wr' type=text ", r);
ap_rputs(
"<tr><td>Route Redirect:</td><td><input name='w_rr' id='w_rr' type=text ", r);
ap_rputs(
"<td><table border='1'><tr><th>Ign</th><th>Drn</th><th>Dis</th><th>Stby</th></tr>\n<tr>", r);
ap_rputs(
"<tr><td colspan=2><input type=submit value='Submit'></td></tr>\n", r);
ap_rvputs(r,
"</table>\n<input type=hidden name='w' id='w' ",
NULL);
ap_rvputs(r,
"<input type=hidden name='nonce' id='nonce' value='",
ap_rputs(
"<h3>Edit balancer settings for ", r);
ap_rputs(
"<td>\n<select name='b_lbm' id='b_lbm'>", r);
ap_rputs(
"<tr><td>Timeout:</td><td><input name='b_tmo' id='b_tmo' type=text ", r);
ap_rputs(
"<tr><td>Failover Attempts:</td><td><input name='b_max' id='b_max' type=text ", r);
ap_rputs(
"<tr><td>Disable Failover:</td>", r);
ap_rputs(
"<tr><td>Sticky Session:</td><td><input name='b_ss' id='b_ss' size=64 type=text ", r);
ap_rputs(
"'> (Use '-' to delete)</td></tr>\n", r);
ap_rputs(
"<tr><td>Add New Worker:</td><td><input name='b_nwrkr' id='b_nwrkr' size=32 type=text>" " Are you sure? <input name='b_wyes' id='b_wyes' type=checkbox value='1'>" ap_rputs(
"<tr><td colspan=2><input type=submit value='Submit'></td></tr>\n", r);
ap_rvputs(r,
"</table>\n<input type=hidden name='b' id='b' ",
NULL);
ap_rvputs(r,
"<input type=hidden name='nonce' id='nonce' value='",
exit(
1);
/* Ugly, but what else? */ "Failed to init balancer %s in child",
exit(
1);
/* Ugly, but what else? */ /* Only the mpm_winnt has child init hook handler. * make sure that we are called after the mpm NULL,
/* create per-directory config structure */ NULL,
/* merge per-directory config structures */ NULL,
/* create per-server config structure */ NULL,
/* merge per-server config structures */ NULL,
/* command apr_table_t */