1/* Licensed to the Apache Software Foundation (ASF) under one or more 2 * contributor license agreements. See the NOTICE file distributed with 3 * this work for additional information regarding copyright ownership. 4 * The ASF licenses this file to You under the Apache License, Version 2.0 5 * (the "License"); you may not use this file except in compliance with 6 * the License. You may obtain a copy of the License at 7 * 8 * http://www.apache.org/licenses/LICENSE-2.0 9 * 10 * Unless required by applicable law or agreed to in writing, software 11 * distributed under the License is distributed on an "AS IS" BASIS, 12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 * See the License for the specific language governing permissions and 14 * limitations under the License. 15 */ 16 17#include "mod_proxy.h" 18#include "scoreboard.h" 19#include "ap_mpm.h" 20#include "apr_version.h" 21#include "ap_hooks.h" 22 23module AP_MODULE_DECLARE_DATA lbmethod_bybusyness_module; 24 25static int (*ap_proxy_retry_worker_fn)(const char *proxy_function, 26 proxy_worker *worker, server_rec *s) = NULL; 27 28static proxy_worker *find_best_bybusyness(proxy_balancer *balancer, 29 request_rec *r) 30{ 31 32 int i; 33 proxy_worker **worker; 34 proxy_worker *mycandidate = NULL; 35 int cur_lbset = 0; 36 int max_lbset = 0; 37 int checking_standby; 38 int checked_standby; 39 40 int total_factor = 0; 41 42 if (!ap_proxy_retry_worker_fn) { 43 ap_proxy_retry_worker_fn = 44 APR_RETRIEVE_OPTIONAL_FN(ap_proxy_retry_worker); 45 if (!ap_proxy_retry_worker_fn) { 46 /* can only happen if mod_proxy isn't loaded */ 47 return NULL; 48 } 49 } 50 51 ap_log_error(APLOG_MARK, APLOG_DEBUG, 0, r->server, APLOGNO(01211) 52 "proxy: Entering bybusyness for BALANCER (%s)", 53 balancer->s->name); 54 55 /* First try to see if we have available candidate */ 56 do { 57 58 checking_standby = checked_standby = 0; 59 while (!mycandidate && !checked_standby) { 60 61 worker = (proxy_worker **)balancer->workers->elts; 62 for (i = 0; i < balancer->workers->nelts; i++, worker++) { 63 if (!checking_standby) { /* first time through */ 64 if ((*worker)->s->lbset > max_lbset) 65 max_lbset = (*worker)->s->lbset; 66 } 67 if ( 68 ((*worker)->s->lbset != cur_lbset) || 69 (checking_standby ? !PROXY_WORKER_IS_STANDBY(*worker) : PROXY_WORKER_IS_STANDBY(*worker)) || 70 (PROXY_WORKER_IS_DRAINING(*worker)) 71 ) { 72 continue; 73 } 74 75 /* If the worker is in error state run 76 * retry on that worker. It will be marked as 77 * operational if the retry timeout is elapsed. 78 * The worker might still be unusable, but we try 79 * anyway. 80 */ 81 if (!PROXY_WORKER_IS_USABLE(*worker)) { 82 ap_proxy_retry_worker_fn("BALANCER", *worker, r->server); 83 } 84 85 /* Take into calculation only the workers that are 86 * not in error state or not disabled. 87 */ 88 if (PROXY_WORKER_IS_USABLE(*worker)) { 89 90 (*worker)->s->lbstatus += (*worker)->s->lbfactor; 91 total_factor += (*worker)->s->lbfactor; 92 93 if (!mycandidate 94 || (*worker)->s->busy < mycandidate->s->busy 95 || ((*worker)->s->busy == mycandidate->s->busy && (*worker)->s->lbstatus > mycandidate->s->lbstatus)) 96 mycandidate = *worker; 97 98 } 99 100 } 101 102 checked_standby = checking_standby++; 103 104 } 105 106 cur_lbset++; 107 108 } while (cur_lbset <= max_lbset && !mycandidate); 109 110 if (mycandidate) { 111 mycandidate->s->lbstatus -= total_factor; 112 ap_log_error(APLOG_MARK, APLOG_DEBUG, 0, r->server, APLOGNO(01212) 113 "proxy: bybusyness selected worker \"%s\" : busy %" APR_SIZE_T_FMT " : lbstatus %d", 114 mycandidate->s->name, mycandidate->s->busy, mycandidate->s->lbstatus); 115 116 } 117 118 return mycandidate; 119 120} 121 122/* assumed to be mutex protected by caller */ 123static apr_status_t reset(proxy_balancer *balancer, server_rec *s) { 124 int i; 125 proxy_worker **worker; 126 worker = (proxy_worker **)balancer->workers->elts; 127 for (i = 0; i < balancer->workers->nelts; i++, worker++) { 128 (*worker)->s->lbstatus = 0; 129 (*worker)->s->busy = 0; 130 } 131 return APR_SUCCESS; 132} 133 134static apr_status_t age(proxy_balancer *balancer, server_rec *s) { 135 return APR_SUCCESS; 136} 137 138static const proxy_balancer_method bybusyness = 139{ 140 "bybusyness", 141 &find_best_bybusyness, 142 NULL, 143 &reset, 144 &age 145}; 146 147 148static void register_hook(apr_pool_t *p) 149{ 150 ap_register_provider(p, PROXY_LBMETHOD, "bybusyness", "0", &bybusyness); 151} 152 153AP_DECLARE_MODULE(lbmethod_bybusyness) = { 154 STANDARD20_MODULE_STUFF, 155 NULL, /* create per-directory config structure */ 156 NULL, /* merge per-directory config structures */ 157 NULL, /* create per-server config structure */ 158 NULL, /* merge per-server config structures */ 159 NULL, /* command apr_table_t */ 160 register_hook /* register hooks */ 161}; 162