static proxy_worker *find_best_hb(proxy_balancer *balancer, request_rec *r) { apr_status_t rv; int i; apr_uint32_t openslots = 0; proxy_worker **worker; hb_server_t *server; apr_array_header_t *up_servers; proxy_worker *mycandidate = NULL; apr_pool_t *tpool; apr_hash_t *servers; lb_hb_ctx_t *ctx = ap_get_module_config(r->server->module_config, &lbmethod_heartbeat_module); if (!ap_proxy_retry_worker_fn) { ap_proxy_retry_worker_fn = APR_RETRIEVE_OPTIONAL_FN(ap_proxy_retry_worker); if (!ap_proxy_retry_worker_fn) { /* can only happen if mod_proxy isn't loaded */ return NULL; } } apr_pool_create(&tpool, r->pool); servers = apr_hash_make(tpool); rv = read_heartbeats(ctx->path, servers, tpool); if (rv) { ap_log_rerror(APLOG_MARK, APLOG_ERR, rv, r, APLOGNO(01213) "lb_heartbeat: Unable to read heartbeats at '%s'", ctx->path); apr_pool_destroy(tpool); return NULL; } up_servers = apr_array_make(tpool, apr_hash_count(servers), sizeof(hb_server_t *)); for (i = 0; i < balancer->workers->nelts; i++) { worker = &APR_ARRAY_IDX(balancer->workers, i, proxy_worker *); server = apr_hash_get(servers, (*worker)->s->hostname, APR_HASH_KEY_STRING); if (!server) { ap_log_rerror(APLOG_MARK, APLOG_DEBUG, rv, r, APLOGNO(01214) "lb_heartbeat: No server for worker %s", (*worker)->s->name); continue; } if (!PROXY_WORKER_IS_USABLE(*worker)) { ap_proxy_retry_worker_fn("BALANCER", *worker, r->server); } if (PROXY_WORKER_IS_USABLE(*worker)) { server->worker = *worker; if (server->seen < LBM_HEARTBEAT_MAX_LASTSEEN) { openslots += server->ready; APR_ARRAY_PUSH(up_servers, hb_server_t *) = server; } } } if (openslots > 0) { apr_uint32_t c = 0; apr_uint32_t pick = 0; pick = ap_random_pick(0, openslots); for (i = 0; i < up_servers->nelts; i++) { server = APR_ARRAY_IDX(up_servers, i, hb_server_t *); if (pick >= c && pick <= c + server->ready) { mycandidate = server->worker; } c += server->ready; } }
static proxy_worker *find_best_roundrobin(proxy_balancer *balancer, request_rec *r) { int i; proxy_worker **worker; proxy_worker *mycandidate = NULL; int checking_standby; int checked_standby; rr_data *ctx; ap_log_error(APLOG_MARK, APLOG_DEBUG, 0, r->server, APLOGNO(01116) "proxy: Entering roundrobin for BALANCER %s (%d)", balancer->s->name, (int)getpid()); /* Example creating a file and writing using APR framework */ apr_pool_t *tpool; apr_pool_create(&tpool, r->pool); ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, r, APLOGNO(05001) "Before Checking Bloom"); const char *packetp = ap_server_root_relative(tpool, "bloom/packet.txt"); const char *filterp = ap_server_root_relative(tpool, "bloom/filter.txt"); const char *debug = ap_server_root_relative(tpool, "bloom/debug.log"); FILE * debugp = fopen(debug, "w+"); int check = checkLastFromFiles(r, filterp, packetp); fprintf(debugp, "CHECK: %d\n", check); fflush(debugp); /*~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~*/ const apr_array_header_t *fields; apr_table_entry_t *e = 0; /*~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~*/ fields = apr_table_elts(r->headers_in); e = (apr_table_entry_t *) fields->elts; for(i = 0; i < fields->nelts; i++) { ap_rprintf(r, "<b>%s</b>: %s<br/>", e[i].key, e[i].val); } fclose(debugp); ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, r, APLOGNO(05002) "After Checking Bloom"); /* The index of the candidate last chosen is stored in ctx->index */ if (!balancer->context) { /* UGLY */ ctx = apr_pcalloc(r->server->process->pconf, sizeof(rr_data)); balancer->context = (void *)ctx; ap_log_error(APLOG_MARK, APLOG_DEBUG, 0, r->server, APLOGNO(01117) "proxy: Creating roundrobin ctx for BALANCER %s (%d)", balancer->s->name, (int)getpid()); } else { ctx = (rr_data *)balancer->context; } ap_log_error(APLOG_MARK, APLOG_DEBUG, 0, r->server, APLOGNO(01118) "proxy: roundrobin index: %d (%d)", ctx->index, (int)getpid()); checking_standby = checked_standby = 0; while (!mycandidate && !checked_standby) { worker = (proxy_worker **)balancer->workers->elts; for (i = 0; i < balancer->workers->nelts; i++, worker++) { if (i < ctx->index) continue; if ( (checking_standby ? !PROXY_WORKER_IS_STANDBY(*worker) : PROXY_WORKER_IS_STANDBY(*worker)) || (PROXY_WORKER_IS_DRAINING(*worker)) ) { continue; } // if (!PROXY_WORKER_IS_USABLE(*worker)) // ap_proxy_retry_worker("BALANCER", *worker, r->server); if (PROXY_WORKER_IS_USABLE(*worker)) { mycandidate = *worker; break; } } checked_standby = checking_standby++; } ctx->index += 1; if (ctx->index >= balancer->workers->nelts) { ctx->index = 0; } return mycandidate; }
static proxy_worker *find_best_byrequests(proxy_balancer *balancer, request_rec *r) { int i; int total_factor = 0; proxy_worker *worker; proxy_worker *mycandidate = NULL; int cur_lbset = 0; int max_lbset = 0; int checking_standby; int checked_standby; ap_log_error(APLOG_MARK, APLOG_DEBUG, 0, r->server, "proxy: Entering byrequests for BALANCER (%s)", balancer->name); /* First try to see if we have available candidate */ do { checking_standby = checked_standby = 0; while (!mycandidate && !checked_standby) { worker = (proxy_worker *)balancer->workers->elts; for (i = 0; i < balancer->workers->nelts; i++, worker++) { if (!checking_standby) { /* first time through */ if (worker->s->lbset > max_lbset) max_lbset = worker->s->lbset; } if (worker->s->lbset > cur_lbset) continue; if ( (checking_standby ? !PROXY_WORKER_IS_STANDBY(worker) : PROXY_WORKER_IS_STANDBY(worker)) ) continue; /* 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)) { worker->s->lbstatus += worker->s->lbfactor; total_factor += worker->s->lbfactor; if (!mycandidate || worker->s->lbstatus > mycandidate->s->lbstatus) mycandidate = worker; } } checked_standby = checking_standby++; } cur_lbset++; } while (cur_lbset <= max_lbset && !mycandidate); if (mycandidate) { mycandidate->s->lbstatus -= total_factor; ap_log_error(APLOG_MARK, APLOG_DEBUG, 0, r->server, "proxy: byrequests selected worker \"%s\" : busy %" APR_SIZE_T_FMT " : lbstatus %d", mycandidate->name, mycandidate->s->busy, mycandidate->s->lbstatus); } return mycandidate; }
/* Manages the loadfactors and member status */ static int balancer_handler(request_rec *r) { void *sconf = r->server->module_config; proxy_server_conf *conf = (proxy_server_conf *) ap_get_module_config(sconf, &proxy_module); proxy_balancer *balancer, *bsel = NULL; proxy_worker *worker, *wsel = NULL; apr_table_t *params = apr_table_make(r->pool, 10); int access_status; int i, n; const char *name; /* is this for us? */ if (strcmp(r->handler, "balancer-manager")) return DECLINED; r->allowed = (AP_METHOD_BIT << M_GET); if (r->method_number != M_GET) return DECLINED; if (r->args) { char *args = apr_pstrdup(r->pool, r->args); char *tok, *val; while (args && *args) { if ((val = ap_strchr(args, '='))) { *val++ = '\0'; if ((tok = ap_strchr(val, '&'))) *tok++ = '\0'; /* * Special case: workers are allowed path information */ if ((access_status = ap_unescape_url(val)) != OK) if (strcmp(args, "w") || (access_status != HTTP_NOT_FOUND)) return access_status; apr_table_setn(params, args, val); args = tok; } else return HTTP_BAD_REQUEST; } } /* Check that the supplied nonce matches this server's nonce; * otherwise ignore all parameters, to prevent a CSRF attack. */ if ((name = apr_table_get(params, "nonce")) == NULL || strcmp(balancer_nonce, name) != 0) { apr_table_clear(params); } if ((name = apr_table_get(params, "b"))) bsel = ap_proxy_get_balancer(r->pool, conf, apr_pstrcat(r->pool, "balancer://", name, NULL)); if ((name = apr_table_get(params, "w"))) { proxy_worker *ws; ws = ap_proxy_get_worker(r->pool, conf, name); if (bsel && ws) { worker = (proxy_worker *)bsel->workers->elts; for (n = 0; n < bsel->workers->nelts; n++) { if (strcasecmp(worker->name, ws->name) == 0) { wsel = worker; break; } ++worker; } } } /* First set the params */ /* * Note that it is not possible set the proxy_balancer because it is not * in shared memory. */ if (wsel) { const char *val; if ((val = apr_table_get(params, "lf"))) { int ival = atoi(val); if (ival >= 1 && ival <= 100) { wsel->s->lbfactor = ival; if (bsel) recalc_factors(bsel); } } if ((val = apr_table_get(params, "wr"))) { if (strlen(val) && strlen(val) < PROXY_WORKER_MAX_ROUTE_SIZ) strcpy(wsel->s->route, val); else *wsel->s->route = '\0'; } if ((val = apr_table_get(params, "rr"))) { if (strlen(val) && strlen(val) < PROXY_WORKER_MAX_ROUTE_SIZ) strcpy(wsel->s->redirect, val); else *wsel->s->redirect = '\0'; } if ((val = apr_table_get(params, "dw"))) { if (!strcasecmp(val, "Disable")) wsel->s->status |= PROXY_WORKER_DISABLED; else if (!strcasecmp(val, "Enable")) wsel->s->status &= ~PROXY_WORKER_DISABLED; } if ((val = apr_table_get(params, "ls"))) { int ival = atoi(val); if (ival >= 0 && ival <= 99) { wsel->s->lbset = ival; } } } if (apr_table_get(params, "xml")) { ap_set_content_type(r, "text/xml"); ap_rputs("<?xml version=\"1.0\" encoding=\"UTF-8\" ?>\n", r); ap_rputs("<httpd:manager xmlns:httpd=\"http://httpd.apache.org\">\n", r); ap_rputs(" <httpd:balancers>\n", r); balancer = (proxy_balancer *)conf->balancers->elts; for (i = 0; i < conf->balancers->nelts; i++) { ap_rputs(" <httpd:balancer>\n", r); ap_rvputs(r, " <httpd:name>", balancer->name, "</httpd:name>\n", NULL); ap_rputs(" <httpd:workers>\n", r); worker = (proxy_worker *)balancer->workers->elts; for (n = 0; n < balancer->workers->nelts; n++) { ap_rputs(" <httpd:worker>\n", r); ap_rvputs(r, " <httpd:scheme>", worker->scheme, "</httpd:scheme>\n", NULL); ap_rvputs(r, " <httpd:hostname>", worker->hostname, "</httpd:hostname>\n", NULL); ap_rprintf(r, " <httpd:loadfactor>%d</httpd:loadfactor>\n", worker->s->lbfactor); ap_rputs(" </httpd:worker>\n", r); ++worker; } ap_rputs(" </httpd:workers>\n", r); ap_rputs(" </httpd:balancer>\n", r); ++balancer; } ap_rputs(" </httpd:balancers>\n", r); ap_rputs("</httpd:manager>", r); } else { ap_set_content_type(r, "text/html; charset=ISO-8859-1"); ap_rputs(DOCTYPE_HTML_3_2 "<html><head><title>Balancer Manager</title></head>\n", r); ap_rputs("<body><h1>Load Balancer Manager for ", r); ap_rvputs(r, ap_get_server_name(r), "</h1>\n\n", NULL); ap_rvputs(r, "<dl><dt>Server Version: ", ap_get_server_description(), "</dt>\n", NULL); ap_rvputs(r, "<dt>Server Built: ", ap_get_server_built(), "\n</dt></dl>\n", NULL); balancer = (proxy_balancer *)conf->balancers->elts; for (i = 0; i < conf->balancers->nelts; i++) { ap_rputs("<hr />\n<h3>LoadBalancer Status for ", r); ap_rvputs(r, balancer->name, "</h3>\n\n", NULL); ap_rputs("\n\n<table border=\"0\" style=\"text-align: left;\"><tr>" "<th>StickySession</th><th>Timeout</th><th>FailoverAttempts</th><th>Method</th>" "</tr>\n<tr>", r); if (balancer->sticky) { ap_rvputs(r, "<td>", balancer->sticky, NULL); } else { ap_rputs("<td> - ", r); } ap_rprintf(r, "</td><td>%" APR_TIME_T_FMT "</td>", apr_time_sec(balancer->timeout)); ap_rprintf(r, "<td>%d</td>\n", balancer->max_attempts); ap_rprintf(r, "<td>%s</td>\n", balancer->lbmethod->name); ap_rputs("</table>\n<br />", r); ap_rputs("\n\n<table border=\"0\" style=\"text-align: left;\"><tr>" "<th>Worker URL</th>" "<th>Route</th><th>RouteRedir</th>" "<th>Factor</th><th>Set</th><th>Status</th>" "<th>Elected</th><th>To</th><th>From</th>" "</tr>\n", r); worker = (proxy_worker *)balancer->workers->elts; for (n = 0; n < balancer->workers->nelts; n++) { char fbuf[50]; ap_rvputs(r, "<tr>\n<td><a href=\"", r->uri, "?b=", balancer->name + sizeof("balancer://") - 1, "&w=", ap_escape_uri(r->pool, worker->name), "&nonce=", balancer_nonce, "\">", NULL); ap_rvputs(r, worker->name, "</a></td>", NULL); ap_rvputs(r, "<td>", ap_escape_html(r->pool, worker->s->route), NULL); ap_rvputs(r, "</td><td>", ap_escape_html(r->pool, worker->s->redirect), NULL); ap_rprintf(r, "</td><td>%d</td>", worker->s->lbfactor); ap_rprintf(r, "<td>%d</td><td>", worker->s->lbset); if (worker->s->status & PROXY_WORKER_DISABLED) ap_rputs("Dis ", r); if (worker->s->status & PROXY_WORKER_IN_ERROR) ap_rputs("Err ", r); if (worker->s->status & PROXY_WORKER_STOPPED) ap_rputs("Stop ", r); if (worker->s->status & PROXY_WORKER_HOT_STANDBY) ap_rputs("Stby ", r); if (PROXY_WORKER_IS_USABLE(worker)) ap_rputs("Ok", r); if (!PROXY_WORKER_IS_INITIALIZED(worker)) ap_rputs("-", r); ap_rputs("</td>", r); ap_rprintf(r, "<td>%" APR_SIZE_T_FMT "</td><td>", worker->s->elected); ap_rputs(apr_strfsize(worker->s->transferred, fbuf), r); ap_rputs("</td><td>", r); ap_rputs(apr_strfsize(worker->s->read, fbuf), r); ap_rputs("</td></tr>\n", r); ++worker; } ap_rputs("</table>\n", r); ++balancer; } ap_rputs("<hr />\n", r); if (wsel && bsel) { ap_rputs("<h3>Edit worker settings for ", r); ap_rvputs(r, wsel->name, "</h3>\n", NULL); ap_rvputs(r, "<form method=\"GET\" action=\"", NULL); ap_rvputs(r, r->uri, "\">\n<dl>", NULL); ap_rputs("<table><tr><td>Load factor:</td><td><input name=\"lf\" type=text ", r); ap_rprintf(r, "value=\"%d\"></td></tr>\n", wsel->s->lbfactor); ap_rputs("<tr><td>LB Set:</td><td><input name=\"ls\" type=text ", r); ap_rprintf(r, "value=\"%d\"></td></tr>\n", wsel->s->lbset); ap_rputs("<tr><td>Route:</td><td><input name=\"wr\" type=text ", r); ap_rvputs(r, "value=\"", ap_escape_html(r->pool, wsel->s->route), NULL); ap_rputs("\"></td></tr>\n", r); ap_rputs("<tr><td>Route Redirect:</td><td><input name=\"rr\" type=text ", r); ap_rvputs(r, "value=\"", ap_escape_html(r->pool, wsel->s->redirect), NULL); ap_rputs("\"></td></tr>\n", r); ap_rputs("<tr><td>Status:</td><td>Disabled: <input name=\"dw\" value=\"Disable\" type=radio", r); if (wsel->s->status & PROXY_WORKER_DISABLED) ap_rputs(" checked", r); ap_rputs("> | Enabled: <input name=\"dw\" value=\"Enable\" type=radio", r); if (!(wsel->s->status & PROXY_WORKER_DISABLED)) ap_rputs(" checked", r); ap_rputs("></td></tr>\n", 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\" ", NULL); ap_rvputs(r, "value=\"", ap_escape_uri(r->pool, wsel->name), "\">\n", NULL); ap_rvputs(r, "<input type=hidden name=\"b\" ", NULL); ap_rvputs(r, "value=\"", bsel->name + sizeof("balancer://") - 1, "\">\n", NULL); ap_rvputs(r, "<input type=hidden name=\"nonce\" value=\"", balancer_nonce, "\">\n", NULL); ap_rvputs(r, "</form>\n", NULL); ap_rputs("<hr />\n", r); } ap_rputs(ap_psignature("",r), r); ap_rputs("</body></html>\n", r); } return OK; }
static int proxy_balancer_pre_request(proxy_worker **worker, proxy_balancer **balancer, request_rec *r, proxy_server_conf *conf, char **url) { int access_status; proxy_worker *runtime; char *route = NULL; char *sticky = NULL; apr_status_t rv; *worker = NULL; /* 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. */ if (!*balancer && !(*balancer = ap_proxy_get_balancer(r->pool, conf, *url))) return DECLINED; /* Step 2: find the session route */ runtime = find_session_route(*balancer, r, &route, &sticky, url); /* Lock the LoadBalancer * XXX: perhaps we need the process lock here */ if ((rv = PROXY_THREAD_LOCK(*balancer)) != APR_SUCCESS) { ap_log_error(APLOG_MARK, APLOG_ERR, rv, r->server, "proxy: BALANCER: (%s). Lock failed for pre_request", (*balancer)->name); return DECLINED; } if (runtime) { int i, total_factor = 0; proxy_worker *workers; /* We have a sticky load balancer * Update the workers status * so that even session routes get * into account. */ workers = (proxy_worker *)(*balancer)->workers->elts; for (i = 0; i < (*balancer)->workers->nelts; i++) { /* Take into calculation only the workers that are * not in error state or not disabled. * * TODO: Abstract the below, since this is dependent * on the LB implementation */ if (PROXY_WORKER_IS_USABLE(workers)) { workers->s->lbstatus += workers->s->lbfactor; total_factor += workers->s->lbfactor; } workers++; } runtime->s->lbstatus -= total_factor; runtime->s->elected++; *worker = runtime; } else if (route && (*balancer)->sticky_force) { int i, member_of = 0; proxy_worker *workers; /* * 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 */ workers = (proxy_worker *)(*balancer)->workers->elts; for (i = 0; i < (*balancer)->workers->nelts; i++) { if (*(workers->s->route) && strcmp(workers->s->route, route) == 0) { member_of = 1; break; } workers++; } if (member_of) { ap_log_error(APLOG_MARK, APLOG_ERR, 0, r->server, "proxy: BALANCER: (%s). All workers are in error state for route (%s)", (*balancer)->name, route); if ((rv = PROXY_THREAD_UNLOCK(*balancer)) != APR_SUCCESS) { ap_log_error(APLOG_MARK, APLOG_ERR, rv, r->server, "proxy: BALANCER: (%s). Unlock failed for pre_request", (*balancer)->name); } return HTTP_SERVICE_UNAVAILABLE; } } if ((rv = PROXY_THREAD_UNLOCK(*balancer)) != APR_SUCCESS) { ap_log_error(APLOG_MARK, APLOG_ERR, rv, r->server, "proxy: BALANCER: (%s). Unlock failed for pre_request", (*balancer)->name); } if (!*worker) { runtime = find_best_worker(*balancer, r); if (!runtime) { ap_log_error(APLOG_MARK, APLOG_ERR, 0, r->server, "proxy: BALANCER: (%s). All workers are in error state", (*balancer)->name); return HTTP_SERVICE_UNAVAILABLE; } if ((*balancer)->sticky && runtime) { /* * 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. */ apr_table_setn(r->subprocess_env, "BALANCER_ROUTE_CHANGED", "1"); } *worker = runtime; } (*worker)->s->busy++; /* Add balancer/worker info to env. */ apr_table_setn(r->subprocess_env, "BALANCER_NAME", (*balancer)->name); apr_table_setn(r->subprocess_env, "BALANCER_WORKER_NAME", (*worker)->name); apr_table_setn(r->subprocess_env, "BALANCER_WORKER_ROUTE", (*worker)->s->route); /* 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) { apr_table_setn(r->notes, "session-sticky", sticky); apr_table_setn(r->notes, "session-route", route); /* Add session info to env. */ apr_table_setn(r->subprocess_env, "BALANCER_SESSION_STICKY", sticky); apr_table_setn(r->subprocess_env, "BALANCER_SESSION_ROUTE", route); } ap_log_error(APLOG_MARK, APLOG_DEBUG, 0, r->server, "proxy: BALANCER (%s) worker (%s) rewritten to %s", (*balancer)->name, (*worker)->name, *url); return access_status; }
/* Find the worker that has the 'route' defined */ static proxy_worker *find_route_worker(proxy_balancer *balancer, const char *route, request_rec *r) { int i; int checking_standby; int checked_standby; proxy_worker *worker; checking_standby = checked_standby = 0; while (!checked_standby) { worker = (proxy_worker *)balancer->workers->elts; for (i = 0; i < balancer->workers->nelts; i++, worker++) { if ( (checking_standby ? !PROXY_WORKER_IS_STANDBY(worker) : PROXY_WORKER_IS_STANDBY(worker)) ) continue; if (*(worker->s->route) && strcmp(worker->s->route, route) == 0) { if (worker && PROXY_WORKER_IS_USABLE(worker)) { return worker; } else { /* * 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. */ ap_proxy_retry_worker("BALANCER", worker, r->server); if (PROXY_WORKER_IS_USABLE(worker)) { return worker; } else { /* * 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. */ if (*worker->s->redirect) { proxy_worker *rworker = NULL; rworker = find_route_worker(balancer, worker->s->redirect, r); /* Check if the redirect worker is usable */ if (rworker && !PROXY_WORKER_IS_USABLE(rworker)) { /* * 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. */ ap_proxy_retry_worker("BALANCER", rworker, r->server); } if (rworker && PROXY_WORKER_IS_USABLE(rworker)) return rworker; } } } } } checked_standby = checking_standby++; } return NULL; }