From e072a2dd866b7cb9f14319b80326a4e7fd16fcdf Mon Sep 17 00:00:00 2001 From: Arno Töll Date: Sun, 8 Jan 2012 22:53:17 +0100 Subject: Imported Upstream version 2.3.16-beta --- modules/proxy/balancers/Makefile.in | 3 + modules/proxy/balancers/config2.m4 | 8 + modules/proxy/balancers/mod_lbmethod_bybusyness.c | 161 +++++++ .../proxy/balancers/mod_lbmethod_bybusyness.dsp | 123 ++++++ modules/proxy/balancers/mod_lbmethod_byrequests.c | 198 +++++++++ .../proxy/balancers/mod_lbmethod_byrequests.dsp | 123 ++++++ modules/proxy/balancers/mod_lbmethod_bytraffic.c | 168 ++++++++ modules/proxy/balancers/mod_lbmethod_bytraffic.dsp | 123 ++++++ modules/proxy/balancers/mod_lbmethod_heartbeat.c | 464 +++++++++++++++++++++ modules/proxy/balancers/mod_lbmethod_heartbeat.dsp | 123 ++++++ 10 files changed, 1494 insertions(+) create mode 100644 modules/proxy/balancers/Makefile.in create mode 100644 modules/proxy/balancers/config2.m4 create mode 100644 modules/proxy/balancers/mod_lbmethod_bybusyness.c create mode 100644 modules/proxy/balancers/mod_lbmethod_bybusyness.dsp create mode 100644 modules/proxy/balancers/mod_lbmethod_byrequests.c create mode 100644 modules/proxy/balancers/mod_lbmethod_byrequests.dsp create mode 100644 modules/proxy/balancers/mod_lbmethod_bytraffic.c create mode 100644 modules/proxy/balancers/mod_lbmethod_bytraffic.dsp create mode 100644 modules/proxy/balancers/mod_lbmethod_heartbeat.c create mode 100644 modules/proxy/balancers/mod_lbmethod_heartbeat.dsp (limited to 'modules/proxy/balancers') diff --git a/modules/proxy/balancers/Makefile.in b/modules/proxy/balancers/Makefile.in new file mode 100644 index 00000000..7c5c149d --- /dev/null +++ b/modules/proxy/balancers/Makefile.in @@ -0,0 +1,3 @@ +# a modules Makefile has no explicit targets -- they will be defined by +# whatever modules are enabled. just grab special.mk to deal with this. +include $(top_srcdir)/build/special.mk diff --git a/modules/proxy/balancers/config2.m4 b/modules/proxy/balancers/config2.m4 new file mode 100644 index 00000000..f7232661 --- /dev/null +++ b/modules/proxy/balancers/config2.m4 @@ -0,0 +1,8 @@ +APACHE_MODPATH_INIT(proxy/balancers) + +APACHE_MODULE(lbmethod_byrequests, Apache proxy Load balancing by request counting, , , $proxy_mods_enable) +APACHE_MODULE(lbmethod_bytraffic, Apache proxy Load balancing by traffic counting, , , $proxy_mods_enable) +APACHE_MODULE(lbmethod_bybusyness, Apache proxy Load balancing by busyness, , , $proxy_mods_enable) +APACHE_MODULE(lbmethod_heartbeat, Apache proxy Load balancing from Heartbeats, , , $proxy_mods_enable) + +APACHE_MODPATH_FINISH diff --git a/modules/proxy/balancers/mod_lbmethod_bybusyness.c b/modules/proxy/balancers/mod_lbmethod_bybusyness.c new file mode 100644 index 00000000..9185f22f --- /dev/null +++ b/modules/proxy/balancers/mod_lbmethod_bybusyness.c @@ -0,0 +1,161 @@ +/* Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "mod_proxy.h" +#include "scoreboard.h" +#include "ap_mpm.h" +#include "apr_version.h" +#include "ap_hooks.h" + +module AP_MODULE_DECLARE_DATA lbmethod_bybusyness_module; + +static int (*ap_proxy_retry_worker_fn)(const char *proxy_function, + proxy_worker *worker, server_rec *s) = NULL; + +static proxy_worker *find_best_bybusyness(proxy_balancer *balancer, + request_rec *r) +{ + + int i; + proxy_worker **worker; + proxy_worker *mycandidate = NULL; + int cur_lbset = 0; + int max_lbset = 0; + int checking_standby; + int checked_standby; + + int total_factor = 0; + + 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; + } + } + + ap_log_error(APLOG_MARK, APLOG_DEBUG, 0, r->server, APLOGNO(01211) + "proxy: Entering bybusyness for BALANCER (%s)", + balancer->s->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) || + (checking_standby ? !PROXY_WORKER_IS_STANDBY(*worker) : PROXY_WORKER_IS_STANDBY(*worker)) || + (PROXY_WORKER_IS_DRAINING(*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_fn("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->busy < mycandidate->s->busy + || ((*worker)->s->busy == mycandidate->s->busy && (*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, APLOGNO(01212) + "proxy: bybusyness selected worker \"%s\" : busy %" APR_SIZE_T_FMT " : lbstatus %d", + mycandidate->s->name, mycandidate->s->busy, mycandidate->s->lbstatus); + + } + + return mycandidate; + +} + +/* assumed to be mutex protected by caller */ +static apr_status_t reset(proxy_balancer *balancer, server_rec *s) { + int i; + proxy_worker **worker; + worker = (proxy_worker **)balancer->workers->elts; + for (i = 0; i < balancer->workers->nelts; i++, worker++) { + (*worker)->s->lbstatus = 0; + (*worker)->s->busy = 0; + } + return APR_SUCCESS; +} + +static apr_status_t age(proxy_balancer *balancer, server_rec *s) { + return APR_SUCCESS; +} + +static const proxy_balancer_method bybusyness = +{ + "bybusyness", + &find_best_bybusyness, + NULL, + &reset, + &age +}; + + +static void register_hook(apr_pool_t *p) +{ + ap_register_provider(p, PROXY_LBMETHOD, "bybusyness", "0", &bybusyness); +} + +AP_DECLARE_MODULE(lbmethod_bybusyness) = { + STANDARD20_MODULE_STUFF, + 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 */ + register_hook /* register hooks */ +}; diff --git a/modules/proxy/balancers/mod_lbmethod_bybusyness.dsp b/modules/proxy/balancers/mod_lbmethod_bybusyness.dsp new file mode 100644 index 00000000..8544600c --- /dev/null +++ b/modules/proxy/balancers/mod_lbmethod_bybusyness.dsp @@ -0,0 +1,123 @@ +# Microsoft Developer Studio Project File - Name="mod_lbmethod_bybusyness" - Package Owner=<4> +# Microsoft Developer Studio Generated Build File, Format Version 6.00 +# ** DO NOT EDIT ** + +# TARGTYPE "Win32 (x86) Dynamic-Link Library" 0x0102 + +CFG=mod_lbmethod_bybusyness - Win32 Release +!MESSAGE This is not a valid makefile. To build this project using NMAKE, +!MESSAGE use the Export Makefile command and run +!MESSAGE +!MESSAGE NMAKE /f "mod_lbmethod_bybusyness.mak". +!MESSAGE +!MESSAGE You can specify a configuration when running NMAKE +!MESSAGE by defining the macro CFG on the command line. For example: +!MESSAGE +!MESSAGE NMAKE /f "mod_lbmethod_bybusyness.mak" CFG="mod_lbmethod_bybusyness - Win32 Release" +!MESSAGE +!MESSAGE Possible choices for configuration are: +!MESSAGE +!MESSAGE "mod_lbmethod_bybusyness - Win32 Release" (based on "Win32 (x86) Dynamic-Link Library") +!MESSAGE "mod_lbmethod_bybusyness - Win32 Debug" (based on "Win32 (x86) Dynamic-Link Library") +!MESSAGE + +# Begin Project +# PROP AllowPerConfigDependencies 0 +# PROP Scc_ProjName "" +# PROP Scc_LocalPath "" +CPP=cl.exe +MTL=midl.exe +RSC=rc.exe + +!IF "$(CFG)" == "mod_lbmethod_bybusyness - Win32 Release" + +# PROP BASE Use_MFC 0 +# PROP BASE Use_Debug_Libraries 0 +# PROP BASE Output_Dir "Release" +# PROP BASE Intermediate_Dir "Release" +# PROP BASE Target_Dir "" +# PROP Use_MFC 0 +# PROP Use_Debug_Libraries 0 +# PROP Output_Dir "Release" +# PROP Intermediate_Dir "Release" +# PROP Ignore_Export_Lib 0 +# PROP Target_Dir "" +# ADD BASE CPP /nologo /MD /W3 /O2 /D "WIN32" /D "NDEBUG" /D "_WINDOWS" /FD /c +# ADD CPP /nologo /MD /W3 /O2 /Oy- /Zi /I ".." /I "../../../include" /I "../../../srclib/apr/include" /I "../../../srclib/apr-util/include" /D "NDEBUG" /D "WIN32" /D "_WINDOWS" /Fd"Release\mod_lbmethod_bybusyness_src" /FD /c +# ADD BASE MTL /nologo /D "NDEBUG" /win32 +# ADD MTL /nologo /D "NDEBUG" /mktyplib203 /win32 +# ADD BASE RSC /l 0x809 /d "NDEBUG" +# ADD RSC /l 0x409 /fo"Release/mod_lbmethod_bybusyness.res" /i "../../../include" /i "../../../srclib/apr/include" /d "NDEBUG" /d BIN_NAME="mod_lbmethod_bybusyness.so" /d LONG_NAME="lbmethod_bybusyness_module for Apache" +BSC32=bscmake.exe +# ADD BASE BSC32 /nologo +# ADD BSC32 /nologo +LINK32=link.exe +# ADD BASE LINK32 kernel32.lib ws2_32.lib mswsock.lib /nologo /subsystem:windows /dll /out:".\Release\mod_lbmethod_bybusyness.so" /base:@..\..\..\os\win32\BaseAddr.ref,mod_lbmethod_bybusyness.so +# ADD LINK32 kernel32.lib ws2_32.lib mswsock.lib /nologo /subsystem:windows /dll /incremental:no /debug /out:".\Release\mod_lbmethod_bybusyness.so" /base:@..\..\..\os\win32\BaseAddr.ref,mod_lbmethod_bybusyness.so /opt:ref +# Begin Special Build Tool +TargetPath=.\Release\mod_lbmethod_bybusyness.so +SOURCE="$(InputPath)" +PostBuild_Desc=Embed .manifest +PostBuild_Cmds=if exist $(TargetPath).manifest mt.exe -manifest $(TargetPath).manifest -outputresource:$(TargetPath);2 +# End Special Build Tool + +!ELSEIF "$(CFG)" == "mod_lbmethod_bybusyness - Win32 Debug" + +# PROP BASE Use_MFC 0 +# PROP BASE Use_Debug_Libraries 1 +# PROP BASE Output_Dir "Debug" +# PROP BASE Intermediate_Dir "Debug" +# PROP BASE Target_Dir "" +# PROP Use_MFC 0 +# PROP Use_Debug_Libraries 1 +# PROP Output_Dir "Debug" +# PROP Intermediate_Dir "Debug" +# PROP Ignore_Export_Lib 0 +# PROP Target_Dir "" +# ADD BASE CPP /nologo /MDd /W3 /EHsc /Zi /Od /D "WIN32" /D "_DEBUG" /D "_WINDOWS" /FD /c +# ADD CPP /nologo /MDd /W3 /EHsc /Zi /Od /I ".." /I "../../../include" /I "../../../srclib/apr/include" /I "../../../srclib/apr-util/include" /D "_DEBUG" /D "WIN32" /D "_WINDOWS" /Fd"Debug\mod_lbmethod_bybusyness_src" /FD /c +# ADD BASE MTL /nologo /D "_DEBUG" /win32 +# ADD MTL /nologo /D "_DEBUG" /mktyplib203 /win32 +# ADD BASE RSC /l 0x809 /d "_DEBUG" +# ADD RSC /l 0x409 /fo"Debug/mod_lbmethod_bybusyness.res" /i "../../../include" /i "../../../srclib/apr/include" /d "_DEBUG" /d BIN_NAME="mod_lbmethod_bybusyness.so" /d LONG_NAME="lbmethod_bybusyness_module for Apache" +BSC32=bscmake.exe +# ADD BASE BSC32 /nologo +# ADD BSC32 /nologo +LINK32=link.exe +# ADD BASE LINK32 kernel32.lib ws2_32.lib mswsock.lib /nologo /subsystem:windows /dll /incremental:no /debug /out:".\Debug\mod_lbmethod_bybusyness.so" /base:@..\..\..\os\win32\BaseAddr.ref,mod_lbmethod_bybusyness.so +# ADD LINK32 kernel32.lib ws2_32.lib mswsock.lib /nologo /subsystem:windows /dll /incremental:no /debug /out:".\Debug\mod_lbmethod_bybusyness.so" /base:@..\..\..\os\win32\BaseAddr.ref,mod_lbmethod_bybusyness.so +# Begin Special Build Tool +TargetPath=.\Debug\mod_lbmethod_bybusyness.so +SOURCE="$(InputPath)" +PostBuild_Desc=Embed .manifest +PostBuild_Cmds=if exist $(TargetPath).manifest mt.exe -manifest $(TargetPath).manifest -outputresource:$(TargetPath);2 +# End Special Build Tool + +!ENDIF + +# Begin Target + +# Name "mod_lbmethod_bybusyness - Win32 Release" +# Name "mod_lbmethod_bybusyness - Win32 Debug" +# Begin Group "Source Files" + +# PROP Default_Filter "cpp;c;cxx;rc;def;r;odl;hpj;bat;for;f90" +# Begin Source File + +SOURCE=.\mod_lbmethod_bybusyness.c +# End Source File +# End Group +# Begin Group "Header Files" + +# PROP Default_Filter ".h" +# Begin Source File + +SOURCE=..\mod_proxy.h +# End Source File +# End Group +# Begin Source File + +SOURCE=..\..\..\build\win32\httpd.rc +# End Source File +# End Target +# End Project diff --git a/modules/proxy/balancers/mod_lbmethod_byrequests.c b/modules/proxy/balancers/mod_lbmethod_byrequests.c new file mode 100644 index 00000000..015de12a --- /dev/null +++ b/modules/proxy/balancers/mod_lbmethod_byrequests.c @@ -0,0 +1,198 @@ +/* Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "mod_proxy.h" +#include "scoreboard.h" +#include "ap_mpm.h" +#include "apr_version.h" +#include "ap_hooks.h" + +module AP_MODULE_DECLARE_DATA lbmethod_byrequests_module; + +static int (*ap_proxy_retry_worker_fn)(const char *proxy_function, + proxy_worker *worker, server_rec *s) = NULL; + +/* + * 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". + * + * lbstatus is "how urgent this worker has to work to fulfill its quota + * of work". + * + * We distribute each worker's work quota to the worker, and then look + * which of them needs to work most urgently (biggest lbstatus). This + * worker is then selected for work, and its lbstatus reduced by the + * total work quota we distributed to all workers. Thus the sum of all + * lbstatus does not change.(*) + * + * If some workers are disabled, the others will + * still be scheduled correctly. + * + * If a balancer is configured as follows: + * + * worker a b c d + * lbfactor 25 25 25 25 + * + * And b gets disabled, the following schedule is produced: + * + * a c d a c d a c d ... + * + * Note that the above lbfactor setting is the *exact* same as: + * + * worker a b c d + * lbfactor 1 1 1 1 + * + * Asymmetric configurations work as one would expect. For + * example: + * + * worker a b c d + * lbfactor 1 1 1 2 + * + * would have a, b and c all handling about the same + * amount of load with d handling twice what a or b + * or c handles individually. So we could see: + * + * b a d c d a c d b d ... + * + */ + +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; + + 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; + } + } + + ap_log_error(APLOG_MARK, APLOG_DEBUG, 0, r->server, APLOGNO(01207) + "proxy: Entering byrequests for BALANCER (%s)", + balancer->s->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) || + (checking_standby ? !PROXY_WORKER_IS_STANDBY(*worker) : PROXY_WORKER_IS_STANDBY(*worker)) || + (PROXY_WORKER_IS_DRAINING(*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_fn("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, APLOGNO(01208) + "proxy: byrequests selected worker \"%s\" : busy %" APR_SIZE_T_FMT " : lbstatus %d", + mycandidate->s->name, mycandidate->s->busy, mycandidate->s->lbstatus); + + } + + return mycandidate; +} + +/* assumed to be mutex protected by caller */ +static apr_status_t reset(proxy_balancer *balancer, server_rec *s) { + int i; + proxy_worker **worker; + worker = (proxy_worker **)balancer->workers->elts; + for (i = 0; i < balancer->workers->nelts; i++, worker++) { + (*worker)->s->lbstatus = 0; + } + return APR_SUCCESS; +} + +static apr_status_t age(proxy_balancer *balancer, server_rec *s) { + return APR_SUCCESS; +} + +/* + * How to add additional lbmethods: + * 1. Create func which determines "best" candidate worker + * (eg: find_best_bytraffic, above) + * 2. Register it as a provider. + */ +static const proxy_balancer_method byrequests = +{ + "byrequests", + &find_best_byrequests, + NULL, + &reset, + &age +}; + +static void register_hook(apr_pool_t *p) +{ + /* Only the mpm_winnt has child init hook handler. + * make sure that we are called after the mpm + * initializes and after the mod_proxy + */ + ap_register_provider(p, PROXY_LBMETHOD, "byrequests", "0", &byrequests); +} + +AP_DECLARE_MODULE(lbmethod_byrequests) = { + STANDARD20_MODULE_STUFF, + 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 */ + register_hook /* register hooks */ +}; diff --git a/modules/proxy/balancers/mod_lbmethod_byrequests.dsp b/modules/proxy/balancers/mod_lbmethod_byrequests.dsp new file mode 100644 index 00000000..cfa90bfe --- /dev/null +++ b/modules/proxy/balancers/mod_lbmethod_byrequests.dsp @@ -0,0 +1,123 @@ +# Microsoft Developer Studio Project File - Name="mod_lbmethod_byrequests" - Package Owner=<4> +# Microsoft Developer Studio Generated Build File, Format Version 6.00 +# ** DO NOT EDIT ** + +# TARGTYPE "Win32 (x86) Dynamic-Link Library" 0x0102 + +CFG=mod_lbmethod_byrequests - Win32 Release +!MESSAGE This is not a valid makefile. To build this project using NMAKE, +!MESSAGE use the Export Makefile command and run +!MESSAGE +!MESSAGE NMAKE /f "mod_lbmethod_byrequests.mak". +!MESSAGE +!MESSAGE You can specify a configuration when running NMAKE +!MESSAGE by defining the macro CFG on the command line. For example: +!MESSAGE +!MESSAGE NMAKE /f "mod_lbmethod_byrequests.mak" CFG="mod_lbmethod_byrequests - Win32 Release" +!MESSAGE +!MESSAGE Possible choices for configuration are: +!MESSAGE +!MESSAGE "mod_lbmethod_byrequests - Win32 Release" (based on "Win32 (x86) Dynamic-Link Library") +!MESSAGE "mod_lbmethod_byrequests - Win32 Debug" (based on "Win32 (x86) Dynamic-Link Library") +!MESSAGE + +# Begin Project +# PROP AllowPerConfigDependencies 0 +# PROP Scc_ProjName "" +# PROP Scc_LocalPath "" +CPP=cl.exe +MTL=midl.exe +RSC=rc.exe + +!IF "$(CFG)" == "mod_lbmethod_byrequests - Win32 Release" + +# PROP BASE Use_MFC 0 +# PROP BASE Use_Debug_Libraries 0 +# PROP BASE Output_Dir "Release" +# PROP BASE Intermediate_Dir "Release" +# PROP BASE Target_Dir "" +# PROP Use_MFC 0 +# PROP Use_Debug_Libraries 0 +# PROP Output_Dir "Release" +# PROP Intermediate_Dir "Release" +# PROP Ignore_Export_Lib 0 +# PROP Target_Dir "" +# ADD BASE CPP /nologo /MD /W3 /O2 /D "WIN32" /D "NDEBUG" /D "_WINDOWS" /FD /c +# ADD CPP /nologo /MD /W3 /O2 /Oy- /Zi /I ".." /I "../../../include" /I "../../../srclib/apr/include" /I "../../../srclib/apr-util/include" /D "NDEBUG" /D "WIN32" /D "_WINDOWS" /Fd"Release\mod_lbmethod_byrequests_src" /FD /c +# ADD BASE MTL /nologo /D "NDEBUG" /win32 +# ADD MTL /nologo /D "NDEBUG" /mktyplib203 /win32 +# ADD BASE RSC /l 0x809 /d "NDEBUG" +# ADD RSC /l 0x409 /fo"Release/mod_lbmethod_byrequests.res" /i "../../../include" /i "../../../srclib/apr/include" /d "NDEBUG" /d BIN_NAME="mod_lbmethod_byrequests.so" /d LONG_NAME="lbmethod_byrequests_module for Apache" +BSC32=bscmake.exe +# ADD BASE BSC32 /nologo +# ADD BSC32 /nologo +LINK32=link.exe +# ADD BASE LINK32 kernel32.lib ws2_32.lib mswsock.lib /nologo /subsystem:windows /dll /out:".\Release\mod_lbmethod_byrequests.so" /base:@..\..\..\os\win32\BaseAddr.ref,mod_lbmethod_byrequests.so +# ADD LINK32 kernel32.lib ws2_32.lib mswsock.lib /nologo /subsystem:windows /dll /incremental:no /debug /out:".\Release\mod_lbmethod_byrequests.so" /base:@..\..\..\os\win32\BaseAddr.ref,mod_lbmethod_byrequests.so /opt:ref +# Begin Special Build Tool +TargetPath=.\Release\mod_lbmethod_byrequests.so +SOURCE="$(InputPath)" +PostBuild_Desc=Embed .manifest +PostBuild_Cmds=if exist $(TargetPath).manifest mt.exe -manifest $(TargetPath).manifest -outputresource:$(TargetPath);2 +# End Special Build Tool + +!ELSEIF "$(CFG)" == "mod_lbmethod_byrequests - Win32 Debug" + +# PROP BASE Use_MFC 0 +# PROP BASE Use_Debug_Libraries 1 +# PROP BASE Output_Dir "Debug" +# PROP BASE Intermediate_Dir "Debug" +# PROP BASE Target_Dir "" +# PROP Use_MFC 0 +# PROP Use_Debug_Libraries 1 +# PROP Output_Dir "Debug" +# PROP Intermediate_Dir "Debug" +# PROP Ignore_Export_Lib 0 +# PROP Target_Dir "" +# ADD BASE CPP /nologo /MDd /W3 /EHsc /Zi /Od /D "WIN32" /D "_DEBUG" /D "_WINDOWS" /FD /c +# ADD CPP /nologo /MDd /W3 /EHsc /Zi /Od /I ".." /I "../../../include" /I "../../../srclib/apr/include" /I "../../../srclib/apr-util/include" /D "_DEBUG" /D "WIN32" /D "_WINDOWS" /Fd"Debug\mod_lbmethod_byrequests_src" /FD /c +# ADD BASE MTL /nologo /D "_DEBUG" /win32 +# ADD MTL /nologo /D "_DEBUG" /mktyplib203 /win32 +# ADD BASE RSC /l 0x809 /d "_DEBUG" +# ADD RSC /l 0x409 /fo"Debug/mod_lbmethod_byrequests.res" /i "../../../include" /i "../../../srclib/apr/include" /d "_DEBUG" /d BIN_NAME="mod_lbmethod_byrequests.so" /d LONG_NAME="lbmethod_byrequests_module for Apache" +BSC32=bscmake.exe +# ADD BASE BSC32 /nologo +# ADD BSC32 /nologo +LINK32=link.exe +# ADD BASE LINK32 kernel32.lib ws2_32.lib mswsock.lib /nologo /subsystem:windows /dll /incremental:no /debug /out:".\Debug\mod_lbmethod_byrequests.so" /base:@..\..\..\os\win32\BaseAddr.ref,mod_lbmethod_byrequests.so +# ADD LINK32 kernel32.lib ws2_32.lib mswsock.lib /nologo /subsystem:windows /dll /incremental:no /debug /out:".\Debug\mod_lbmethod_byrequests.so" /base:@..\..\..\os\win32\BaseAddr.ref,mod_lbmethod_byrequests.so +# Begin Special Build Tool +TargetPath=.\Debug\mod_lbmethod_byrequests.so +SOURCE="$(InputPath)" +PostBuild_Desc=Embed .manifest +PostBuild_Cmds=if exist $(TargetPath).manifest mt.exe -manifest $(TargetPath).manifest -outputresource:$(TargetPath);2 +# End Special Build Tool + +!ENDIF + +# Begin Target + +# Name "mod_lbmethod_byrequests - Win32 Release" +# Name "mod_lbmethod_byrequests - Win32 Debug" +# Begin Group "Source Files" + +# PROP Default_Filter "cpp;c;cxx;rc;def;r;odl;hpj;bat;for;f90" +# Begin Source File + +SOURCE=.\mod_lbmethod_byrequests.c +# End Source File +# End Group +# Begin Group "Header Files" + +# PROP Default_Filter ".h" +# Begin Source File + +SOURCE=..\mod_proxy.h +# End Source File +# End Group +# Begin Source File + +SOURCE=..\..\..\build\win32\httpd.rc +# End Source File +# End Target +# End Project diff --git a/modules/proxy/balancers/mod_lbmethod_bytraffic.c b/modules/proxy/balancers/mod_lbmethod_bytraffic.c new file mode 100644 index 00000000..ee245155 --- /dev/null +++ b/modules/proxy/balancers/mod_lbmethod_bytraffic.c @@ -0,0 +1,168 @@ +/* Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "mod_proxy.h" +#include "scoreboard.h" +#include "ap_mpm.h" +#include "apr_version.h" +#include "ap_hooks.h" + +module AP_MODULE_DECLARE_DATA lbmethod_bytraffic_module; + +static int (*ap_proxy_retry_worker_fn)(const char *proxy_function, + proxy_worker *worker, server_rec *s) = NULL; + +/* + * 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 *mycandidate = NULL; + int cur_lbset = 0; + int max_lbset = 0; + int checking_standby; + int checked_standby; + + 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; + } + } + + ap_log_error(APLOG_MARK, APLOG_DEBUG, 0, r->server, APLOGNO(01209) + "proxy: Entering bytraffic for BALANCER (%s)", + balancer->s->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) || + (checking_standby ? !PROXY_WORKER_IS_STANDBY(*worker) : PROXY_WORKER_IS_STANDBY(*worker)) || + (PROXY_WORKER_IS_DRAINING(*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_fn("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 (!mycandidate || mytraffic < curmin) { + mycandidate = *worker; + curmin = mytraffic; + } + } + } + checked_standby = checking_standby++; + } + cur_lbset++; + } while (cur_lbset <= max_lbset && !mycandidate); + + if (mycandidate) { + ap_log_error(APLOG_MARK, APLOG_DEBUG, 0, r->server, APLOGNO(01210) + "proxy: bytraffic selected worker \"%s\" : busy %" APR_SIZE_T_FMT, + mycandidate->s->name, mycandidate->s->busy); + + } + + return mycandidate; +} + +/* assumed to be mutex protected by caller */ +static apr_status_t reset(proxy_balancer *balancer, server_rec *s) { + int i; + proxy_worker **worker; + worker = (proxy_worker **)balancer->workers->elts; + for (i = 0; i < balancer->workers->nelts; i++, worker++) { + (*worker)->s->lbstatus = 0; + (*worker)->s->busy = 0; + (*worker)->s->transferred = 0; + (*worker)->s->read = 0; + } + return APR_SUCCESS; +} + +static apr_status_t age(proxy_balancer *balancer, server_rec *s) { + return APR_SUCCESS; +} + +static const proxy_balancer_method bytraffic = +{ + "bytraffic", + &find_best_bytraffic, + NULL, + &reset, + &age +}; + +static void register_hook(apr_pool_t *p) +{ + /* Only the mpm_winnt has child init hook handler. + * make sure that we are called after the mpm + * initializes and after the mod_proxy + */ + ap_register_provider(p, PROXY_LBMETHOD, "bytraffic", "0", &bytraffic); +} + +AP_DECLARE_MODULE(lbmethod_bytraffic) = { + STANDARD20_MODULE_STUFF, + 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 */ + register_hook /* register hooks */ +}; diff --git a/modules/proxy/balancers/mod_lbmethod_bytraffic.dsp b/modules/proxy/balancers/mod_lbmethod_bytraffic.dsp new file mode 100644 index 00000000..3b53fd9c --- /dev/null +++ b/modules/proxy/balancers/mod_lbmethod_bytraffic.dsp @@ -0,0 +1,123 @@ +# Microsoft Developer Studio Project File - Name="mod_lbmethod_bytraffic" - Package Owner=<4> +# Microsoft Developer Studio Generated Build File, Format Version 6.00 +# ** DO NOT EDIT ** + +# TARGTYPE "Win32 (x86) Dynamic-Link Library" 0x0102 + +CFG=mod_lbmethod_bytraffic - Win32 Release +!MESSAGE This is not a valid makefile. To build this project using NMAKE, +!MESSAGE use the Export Makefile command and run +!MESSAGE +!MESSAGE NMAKE /f "mod_lbmethod_bytraffic.mak". +!MESSAGE +!MESSAGE You can specify a configuration when running NMAKE +!MESSAGE by defining the macro CFG on the command line. For example: +!MESSAGE +!MESSAGE NMAKE /f "mod_lbmethod_bytraffic.mak" CFG="mod_lbmethod_bytraffic - Win32 Release" +!MESSAGE +!MESSAGE Possible choices for configuration are: +!MESSAGE +!MESSAGE "mod_lbmethod_bytraffic - Win32 Release" (based on "Win32 (x86) Dynamic-Link Library") +!MESSAGE "mod_lbmethod_bytraffic - Win32 Debug" (based on "Win32 (x86) Dynamic-Link Library") +!MESSAGE + +# Begin Project +# PROP AllowPerConfigDependencies 0 +# PROP Scc_ProjName "" +# PROP Scc_LocalPath "" +CPP=cl.exe +MTL=midl.exe +RSC=rc.exe + +!IF "$(CFG)" == "mod_lbmethod_bytraffic - Win32 Release" + +# PROP BASE Use_MFC 0 +# PROP BASE Use_Debug_Libraries 0 +# PROP BASE Output_Dir "Release" +# PROP BASE Intermediate_Dir "Release" +# PROP BASE Target_Dir "" +# PROP Use_MFC 0 +# PROP Use_Debug_Libraries 0 +# PROP Output_Dir "Release" +# PROP Intermediate_Dir "Release" +# PROP Ignore_Export_Lib 0 +# PROP Target_Dir "" +# ADD BASE CPP /nologo /MD /W3 /O2 /D "WIN32" /D "NDEBUG" /D "_WINDOWS" /FD /c +# ADD CPP /nologo /MD /W3 /O2 /Oy- /Zi /I ".." /I "../../../include" /I "../../../srclib/apr/include" /I "../../../srclib/apr-util/include" /D "NDEBUG" /D "WIN32" /D "_WINDOWS" /Fd"Release\mod_lbmethod_bytraffic_src" /FD /c +# ADD BASE MTL /nologo /D "NDEBUG" /win32 +# ADD MTL /nologo /D "NDEBUG" /mktyplib203 /win32 +# ADD BASE RSC /l 0x809 /d "NDEBUG" +# ADD RSC /l 0x409 /fo"Release/mod_lbmethod_bytraffic.res" /i "../../../include" /i "../../../srclib/apr/include" /d "NDEBUG" /d BIN_NAME="mod_lbmethod_bytraffic.so" /d LONG_NAME="lbmethod_bytraffic_module for Apache" +BSC32=bscmake.exe +# ADD BASE BSC32 /nologo +# ADD BSC32 /nologo +LINK32=link.exe +# ADD BASE LINK32 kernel32.lib ws2_32.lib mswsock.lib /nologo /subsystem:windows /dll /out:".\Release\mod_lbmethod_bytraffic.so" /base:@..\..\..\os\win32\BaseAddr.ref,mod_lbmethod_bytraffic.so +# ADD LINK32 kernel32.lib ws2_32.lib mswsock.lib /nologo /subsystem:windows /dll /incremental:no /debug /out:".\Release\mod_lbmethod_bytraffic.so" /base:@..\..\..\os\win32\BaseAddr.ref,mod_lbmethod_bytraffic.so /opt:ref +# Begin Special Build Tool +TargetPath=.\Release\mod_lbmethod_bytraffic.so +SOURCE="$(InputPath)" +PostBuild_Desc=Embed .manifest +PostBuild_Cmds=if exist $(TargetPath).manifest mt.exe -manifest $(TargetPath).manifest -outputresource:$(TargetPath);2 +# End Special Build Tool + +!ELSEIF "$(CFG)" == "mod_lbmethod_bytraffic - Win32 Debug" + +# PROP BASE Use_MFC 0 +# PROP BASE Use_Debug_Libraries 1 +# PROP BASE Output_Dir "Debug" +# PROP BASE Intermediate_Dir "Debug" +# PROP BASE Target_Dir "" +# PROP Use_MFC 0 +# PROP Use_Debug_Libraries 1 +# PROP Output_Dir "Debug" +# PROP Intermediate_Dir "Debug" +# PROP Ignore_Export_Lib 0 +# PROP Target_Dir "" +# ADD BASE CPP /nologo /MDd /W3 /EHsc /Zi /Od /D "WIN32" /D "_DEBUG" /D "_WINDOWS" /FD /c +# ADD CPP /nologo /MDd /W3 /EHsc /Zi /Od /I ".." /I "../../../include" /I "../../../srclib/apr/include" /I "../../../srclib/apr-util/include" /D "_DEBUG" /D "WIN32" /D "_WINDOWS" /Fd"Debug\mod_lbmethod_bytraffic_src" /FD /c +# ADD BASE MTL /nologo /D "_DEBUG" /win32 +# ADD MTL /nologo /D "_DEBUG" /mktyplib203 /win32 +# ADD BASE RSC /l 0x809 /d "_DEBUG" +# ADD RSC /l 0x409 /fo"Debug/mod_lbmethod_bytraffic.res" /i "../../../include" /i "../../../srclib/apr/include" /d "_DEBUG" /d BIN_NAME="mod_lbmethod_bytraffic.so" /d LONG_NAME="lbmethod_bytraffic_module for Apache" +BSC32=bscmake.exe +# ADD BASE BSC32 /nologo +# ADD BSC32 /nologo +LINK32=link.exe +# ADD BASE LINK32 kernel32.lib ws2_32.lib mswsock.lib /nologo /subsystem:windows /dll /incremental:no /debug /out:".\Debug\mod_lbmethod_bytraffic.so" /base:@..\..\..\os\win32\BaseAddr.ref,mod_lbmethod_bytraffic.so +# ADD LINK32 kernel32.lib ws2_32.lib mswsock.lib /nologo /subsystem:windows /dll /incremental:no /debug /out:".\Debug\mod_lbmethod_bytraffic.so" /base:@..\..\..\os\win32\BaseAddr.ref,mod_lbmethod_bytraffic.so +# Begin Special Build Tool +TargetPath=.\Debug\mod_lbmethod_bytraffic.so +SOURCE="$(InputPath)" +PostBuild_Desc=Embed .manifest +PostBuild_Cmds=if exist $(TargetPath).manifest mt.exe -manifest $(TargetPath).manifest -outputresource:$(TargetPath);2 +# End Special Build Tool + +!ENDIF + +# Begin Target + +# Name "mod_lbmethod_bytraffic - Win32 Release" +# Name "mod_lbmethod_bytraffic - Win32 Debug" +# Begin Group "Source Files" + +# PROP Default_Filter "cpp;c;cxx;rc;def;r;odl;hpj;bat;for;f90" +# Begin Source File + +SOURCE=.\mod_lbmethod_bytraffic.c +# End Source File +# End Group +# Begin Group "Header Files" + +# PROP Default_Filter ".h" +# Begin Source File + +SOURCE=..\mod_proxy.h +# End Source File +# End Group +# Begin Source File + +SOURCE=..\..\..\build\win32\httpd.rc +# End Source File +# End Target +# End Project diff --git a/modules/proxy/balancers/mod_lbmethod_heartbeat.c b/modules/proxy/balancers/mod_lbmethod_heartbeat.c new file mode 100644 index 00000000..26c81584 --- /dev/null +++ b/modules/proxy/balancers/mod_lbmethod_heartbeat.c @@ -0,0 +1,464 @@ +/* Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "mod_proxy.h" +#include "scoreboard.h" +#include "ap_mpm.h" +#include "apr_version.h" +#include "ap_hooks.h" +#include "ap_slotmem.h" +#include "heartbeat.h" + +#ifndef LBM_HEARTBEAT_MAX_LASTSEEN +/* If we haven't seen a heartbeat in the last N seconds, don't count this IP + * as allive. + */ +#define LBM_HEARTBEAT_MAX_LASTSEEN (10) +#endif + +module AP_MODULE_DECLARE_DATA lbmethod_heartbeat_module; + +static int (*ap_proxy_retry_worker_fn)(const char *proxy_function, + proxy_worker *worker, server_rec *s) = NULL; + +static const ap_slotmem_provider_t *storage = NULL; +static ap_slotmem_instance_t *hm_serversmem = NULL; + +/* + * configuration structure + * path: path of the file where the heartbeat information is stored. + */ +typedef struct lb_hb_ctx_t +{ + const char *path; +} lb_hb_ctx_t; + +typedef struct hb_server_t { + const char *ip; + int busy; + int ready; + int port; + int id; + apr_time_t seen; + proxy_worker *worker; +} hb_server_t; + +typedef struct ctx_servers { + apr_time_t now; + apr_hash_t *servers; +} ctx_servers_t; + +static void +argstr_to_table(apr_pool_t *p, char *str, apr_table_t *parms) +{ + char *key; + char *value; + char *strtok_state; + + key = apr_strtok(str, "&", &strtok_state); + while (key) { + value = strchr(key, '='); + if (value) { + *value = '\0'; /* Split the string in two */ + value++; /* Skip passed the = */ + } + else { + value = "1"; + } + ap_unescape_url(key); + ap_unescape_url(value); + apr_table_set(parms, key, value); + /* + ap_log_rerror(APLOG_MARK, APLOG_DEBUG, 0, r, + "Found query arg: %s = %s", key, value); + */ + key = apr_strtok(NULL, "&", &strtok_state); + } +} + +static apr_status_t readfile_heartbeats(const char *path, apr_hash_t *servers, + apr_pool_t *pool) +{ + apr_finfo_t fi; + apr_status_t rv; + apr_file_t *fp; + + if (!path) { + return APR_SUCCESS; + } + + rv = apr_file_open(&fp, path, APR_READ|APR_BINARY|APR_BUFFERED, + APR_OS_DEFAULT, pool); + + if (rv) { + return rv; + } + + rv = apr_file_info_get(&fi, APR_FINFO_SIZE, fp); + + if (rv) { + return rv; + } + + { + char *t; + int lineno = 0; + apr_bucket_alloc_t *ba = apr_bucket_alloc_create(pool); + apr_bucket_brigade *bb = apr_brigade_create(pool, ba); + apr_bucket_brigade *tmpbb = apr_brigade_create(pool, ba); + apr_table_t *hbt = apr_table_make(pool, 10); + + apr_brigade_insert_file(bb, fp, 0, fi.size, pool); + + do { + hb_server_t *server; + char buf[4096]; + apr_size_t bsize = sizeof(buf); + const char *ip; + + apr_brigade_cleanup(tmpbb); + + if (APR_BRIGADE_EMPTY(bb)) { + break; + } + + rv = apr_brigade_split_line(tmpbb, bb, + APR_BLOCK_READ, sizeof(buf)); + lineno++; + + if (rv) { + return rv; + } + + apr_brigade_flatten(tmpbb, buf, &bsize); + + if (bsize == 0) { + break; + } + + buf[bsize - 1] = 0; + + /* comment */ + if (buf[0] == '#') { + continue; + } + + /* line format: \n */ + t = strchr(buf, ' '); + if (!t) { + continue; + } + + ip = apr_pstrndup(pool, buf, t - buf); + t++; + + server = apr_hash_get(servers, ip, APR_HASH_KEY_STRING); + + if (server == NULL) { + server = apr_pcalloc(pool, sizeof(hb_server_t)); + server->ip = ip; + server->port = 80; + server->seen = -1; + + apr_hash_set(servers, server->ip, APR_HASH_KEY_STRING, server); + } + + apr_table_clear(hbt); + + argstr_to_table(pool, apr_pstrdup(pool, t), hbt); + + if (apr_table_get(hbt, "busy")) { + server->busy = atoi(apr_table_get(hbt, "busy")); + } + + if (apr_table_get(hbt, "ready")) { + server->ready = atoi(apr_table_get(hbt, "ready")); + } + + if (apr_table_get(hbt, "lastseen")) { + server->seen = atoi(apr_table_get(hbt, "lastseen")); + } + + if (apr_table_get(hbt, "port")) { + server->port = atoi(apr_table_get(hbt, "port")); + } + + if (server->busy == 0 && server->ready != 0) { + /* Server has zero threads active, but lots of them ready, + * it likely just started up, so lets /4 the number ready, + * to prevent us from completely flooding it with all new + * requests. + */ + server->ready = server->ready / 4; + } + + } while (1); + } + + return APR_SUCCESS; +} + +static apr_status_t hm_read(void* mem, void *data, apr_pool_t *pool) +{ + hm_slot_server_t *slotserver = (hm_slot_server_t *) mem; + ctx_servers_t *ctx = (ctx_servers_t *) data; + apr_hash_t *servers = (apr_hash_t *) ctx->servers; + hb_server_t *server = apr_hash_get(servers, slotserver->ip, APR_HASH_KEY_STRING); + if (server == NULL) { + server = apr_pcalloc(pool, sizeof(hb_server_t)); + server->ip = apr_pstrdup(pool, slotserver->ip); + server->seen = -1; + + apr_hash_set(servers, server->ip, APR_HASH_KEY_STRING, server); + + } + server->busy = slotserver->busy; + server->ready = slotserver->ready; + server->seen = apr_time_sec(ctx->now - slotserver->seen); + server->id = slotserver->id; + if (server->busy == 0 && server->ready != 0) { + server->ready = server->ready / 4; + } + return APR_SUCCESS; +} +static apr_status_t readslot_heartbeats(ctx_servers_t *ctx, + apr_pool_t *pool) +{ + storage->doall(hm_serversmem, hm_read, ctx, pool); + return APR_SUCCESS; +} + + +static apr_status_t read_heartbeats(const char *path, apr_hash_t *servers, + apr_pool_t *pool) +{ + apr_status_t rv; + if (hm_serversmem) { + ctx_servers_t ctx; + ctx.now = apr_time_now(); + ctx.servers = servers; + rv = readslot_heartbeats(&ctx, pool); + } else + rv = readfile_heartbeats(path, servers, pool); + return rv; +} + +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; + } + } + + apr_pool_destroy(tpool); + + return mycandidate; +} + +static apr_status_t reset(proxy_balancer *balancer, server_rec *s) { + return APR_SUCCESS; +} + +static apr_status_t age(proxy_balancer *balancer, server_rec *s) { + return APR_SUCCESS; +} + +static const proxy_balancer_method heartbeat = +{ + "heartbeat", + &find_best_hb, + NULL, + &reset, + &age +}; + +static int lb_hb_init(apr_pool_t *p, apr_pool_t *plog, + apr_pool_t *ptemp, server_rec *s) +{ + apr_size_t size; + unsigned int num; + lb_hb_ctx_t *ctx = ap_get_module_config(s->module_config, + &lbmethod_heartbeat_module); + + /* do nothing on first call */ + if (ap_state_query(AP_SQ_MAIN_STATE) == AP_SQ_MS_CREATE_PRE_CONFIG) + return OK; + + storage = ap_lookup_provider(AP_SLOTMEM_PROVIDER_GROUP, "shm", + AP_SLOTMEM_PROVIDER_VERSION); + if (!storage) { + ap_log_error(APLOG_MARK, APLOG_NOTICE, 0, s, APLOGNO(02281) + "Failed to lookup provider 'shm' for '%s'. Maybe you " + "need to load mod_slotmem_shm?", + AP_SLOTMEM_PROVIDER_GROUP); + return OK; + } + + /* Try to use a slotmem created by mod_heartmonitor */ + storage->attach(&hm_serversmem, "mod_heartmonitor", &size, &num, p); + if (!hm_serversmem) + ap_log_error(APLOG_MARK, APLOG_NOTICE, 0, s, APLOGNO(02282) + "No slotmem from mod_heartmonitor"); + else + ap_log_error(APLOG_MARK, APLOG_NOTICE, 0, s, APLOGNO(02283) + "Using slotmem from mod_heartmonitor"); + + if (hm_serversmem) + ctx->path = "(slotmem)"; + + return OK; +} + +static void register_hooks(apr_pool_t *p) +{ + static const char * const aszPred[]={ "mod_heartmonitor.c", NULL }; + ap_register_provider(p, PROXY_LBMETHOD, "heartbeat", "0", &heartbeat); + ap_hook_post_config(lb_hb_init, aszPred, NULL, APR_HOOK_MIDDLE); +} + +static void *lb_hb_create_config(apr_pool_t *p, server_rec *s) +{ + lb_hb_ctx_t *ctx = (lb_hb_ctx_t *) apr_palloc(p, sizeof(lb_hb_ctx_t)); + + ctx->path = ap_server_root_relative(p, "logs/hb.dat"); + + return ctx; +} + +static void *lb_hb_merge_config(apr_pool_t *p, void *basev, void *overridesv) +{ + lb_hb_ctx_t *ps = apr_pcalloc(p, sizeof(lb_hb_ctx_t)); + lb_hb_ctx_t *base = (lb_hb_ctx_t *) basev; + lb_hb_ctx_t *overrides = (lb_hb_ctx_t *) overridesv; + + if (overrides->path) { + ps->path = apr_pstrdup(p, overrides->path); + } + else { + ps->path = apr_pstrdup(p, base->path); + } + + return ps; +} + +static const char *cmd_lb_hb_storage(cmd_parms *cmd, + void *dconf, const char *path) +{ + apr_pool_t *p = cmd->pool; + lb_hb_ctx_t *ctx = + (lb_hb_ctx_t *) ap_get_module_config(cmd->server->module_config, + &lbmethod_heartbeat_module); + + const char *err = ap_check_cmd_context(cmd, GLOBAL_ONLY); + + if (err != NULL) { + return err; + } + + ctx->path = ap_server_root_relative(p, path); + + return NULL; +} + +static const command_rec cmds[] = { + AP_INIT_TAKE1("HeartbeatStorage", cmd_lb_hb_storage, NULL, RSRC_CONF, + "Path to read heartbeat data."), + {NULL} +}; + +AP_DECLARE_MODULE(lbmethod_heartbeat) = { + STANDARD20_MODULE_STUFF, + NULL, /* create per-directory config structure */ + NULL, /* merge per-directory config structures */ + lb_hb_create_config, /* create per-server config structure */ + lb_hb_merge_config, /* merge per-server config structures */ + cmds, /* command apr_table_t */ + register_hooks /* register hooks */ +}; diff --git a/modules/proxy/balancers/mod_lbmethod_heartbeat.dsp b/modules/proxy/balancers/mod_lbmethod_heartbeat.dsp new file mode 100644 index 00000000..cb003b09 --- /dev/null +++ b/modules/proxy/balancers/mod_lbmethod_heartbeat.dsp @@ -0,0 +1,123 @@ +# Microsoft Developer Studio Project File - Name="mod_lbmethod_heartbeat" - Package Owner=<4> +# Microsoft Developer Studio Generated Build File, Format Version 6.00 +# ** DO NOT EDIT ** + +# TARGTYPE "Win32 (x86) Dynamic-Link Library" 0x0102 + +CFG=mod_lbmethod_heartbeat - Win32 Release +!MESSAGE This is not a valid makefile. To build this project using NMAKE, +!MESSAGE use the Export Makefile command and run +!MESSAGE +!MESSAGE NMAKE /f "mod_lbmethod_heartbeat.mak". +!MESSAGE +!MESSAGE You can specify a configuration when running NMAKE +!MESSAGE by defining the macro CFG on the command line. For example: +!MESSAGE +!MESSAGE NMAKE /f "mod_lbmethod_heartbeat.mak" CFG="mod_lbmethod_heartbeat - Win32 Release" +!MESSAGE +!MESSAGE Possible choices for configuration are: +!MESSAGE +!MESSAGE "mod_lbmethod_heartbeat - Win32 Release" (based on "Win32 (x86) Dynamic-Link Library") +!MESSAGE "mod_lbmethod_heartbeat - Win32 Debug" (based on "Win32 (x86) Dynamic-Link Library") +!MESSAGE + +# Begin Project +# PROP AllowPerConfigDependencies 0 +# PROP Scc_ProjName "" +# PROP Scc_LocalPath "" +CPP=cl.exe +MTL=midl.exe +RSC=rc.exe + +!IF "$(CFG)" == "mod_lbmethod_heartbeat - Win32 Release" + +# PROP BASE Use_MFC 0 +# PROP BASE Use_Debug_Libraries 0 +# PROP BASE Output_Dir "Release" +# PROP BASE Intermediate_Dir "Release" +# PROP BASE Target_Dir "" +# PROP Use_MFC 0 +# PROP Use_Debug_Libraries 0 +# PROP Output_Dir "Release" +# PROP Intermediate_Dir "Release" +# PROP Ignore_Export_Lib 0 +# PROP Target_Dir "" +# ADD BASE CPP /nologo /MD /W3 /O2 /D "WIN32" /D "NDEBUG" /D "_WINDOWS" /FD /c +# ADD CPP /nologo /MD /W3 /O2 /Oy- /Zi /I ".." /I "../../../include" /I "../../../srclib/apr/include" /I "../../../srclib/apr-util/include" /D "NDEBUG" /D "WIN32" /D "_WINDOWS" /Fd"Release\mod_lbmethod_heartbeat_src" /FD /c +# ADD BASE MTL /nologo /D "NDEBUG" /win32 +# ADD MTL /nologo /D "NDEBUG" /mktyplib203 /win32 +# ADD BASE RSC /l 0x809 /d "NDEBUG" +# ADD RSC /l 0x409 /fo"Release/mod_lbmethod_heartbeat.res" /i "../../../include" /i "../../../srclib/apr/include" /d "NDEBUG" /d BIN_NAME="mod_lbmethod_heartbeat.so" /d LONG_NAME="lbmethod_heartbeat_module for Apache" +BSC32=bscmake.exe +# ADD BASE BSC32 /nologo +# ADD BSC32 /nologo +LINK32=link.exe +# ADD BASE LINK32 kernel32.lib ws2_32.lib mswsock.lib /nologo /subsystem:windows /dll /out:".\Release\mod_lbmethod_heartbeat.so" /base:@..\..\..\os\win32\BaseAddr.ref,mod_lbmethod_heartbeat.so +# ADD LINK32 kernel32.lib ws2_32.lib mswsock.lib /nologo /subsystem:windows /dll /incremental:no /debug /out:".\Release\mod_lbmethod_heartbeat.so" /base:@..\..\..\os\win32\BaseAddr.ref,mod_lbmethod_heartbeat.so /opt:ref +# Begin Special Build Tool +TargetPath=.\Release\mod_lbmethod_heartbeat.so +SOURCE="$(InputPath)" +PostBuild_Desc=Embed .manifest +PostBuild_Cmds=if exist $(TargetPath).manifest mt.exe -manifest $(TargetPath).manifest -outputresource:$(TargetPath);2 +# End Special Build Tool + +!ELSEIF "$(CFG)" == "mod_lbmethod_heartbeat - Win32 Debug" + +# PROP BASE Use_MFC 0 +# PROP BASE Use_Debug_Libraries 1 +# PROP BASE Output_Dir "Debug" +# PROP BASE Intermediate_Dir "Debug" +# PROP BASE Target_Dir "" +# PROP Use_MFC 0 +# PROP Use_Debug_Libraries 1 +# PROP Output_Dir "Debug" +# PROP Intermediate_Dir "Debug" +# PROP Ignore_Export_Lib 0 +# PROP Target_Dir "" +# ADD BASE CPP /nologo /MDd /W3 /EHsc /Zi /Od /D "WIN32" /D "_DEBUG" /D "_WINDOWS" /FD /c +# ADD CPP /nologo /MDd /W3 /EHsc /Zi /Od /I ".." /I "../../../include" /I "../../../srclib/apr/include" /I "../../../srclib/apr-util/include" /D "_DEBUG" /D "WIN32" /D "_WINDOWS" /Fd"Debug\mod_lbmethod_heartbeat_src" /FD /c +# ADD BASE MTL /nologo /D "_DEBUG" /win32 +# ADD MTL /nologo /D "_DEBUG" /mktyplib203 /win32 +# ADD BASE RSC /l 0x809 /d "_DEBUG" +# ADD RSC /l 0x409 /fo"Debug/mod_lbmethod_heartbeat.res" /i "../../../include" /i "../../../srclib/apr/include" /d "_DEBUG" /d BIN_NAME="mod_lbmethod_heartbeat.so" /d LONG_NAME="lbmethod_heartbeat_module for Apache" +BSC32=bscmake.exe +# ADD BASE BSC32 /nologo +# ADD BSC32 /nologo +LINK32=link.exe +# ADD BASE LINK32 kernel32.lib ws2_32.lib mswsock.lib /nologo /subsystem:windows /dll /incremental:no /debug /out:".\Debug\mod_lbmethod_heartbeat.so" /base:@..\..\..\os\win32\BaseAddr.ref,mod_lbmethod_heartbeat.so +# ADD LINK32 kernel32.lib ws2_32.lib mswsock.lib /nologo /subsystem:windows /dll /incremental:no /debug /out:".\Debug\mod_lbmethod_heartbeat.so" /base:@..\..\..\os\win32\BaseAddr.ref,mod_lbmethod_heartbeat.so +# Begin Special Build Tool +TargetPath=.\Debug\mod_lbmethod_heartbeat.so +SOURCE="$(InputPath)" +PostBuild_Desc=Embed .manifest +PostBuild_Cmds=if exist $(TargetPath).manifest mt.exe -manifest $(TargetPath).manifest -outputresource:$(TargetPath);2 +# End Special Build Tool + +!ENDIF + +# Begin Target + +# Name "mod_lbmethod_heartbeat - Win32 Release" +# Name "mod_lbmethod_heartbeat - Win32 Debug" +# Begin Group "Source Files" + +# PROP Default_Filter "cpp;c;cxx;rc;def;r;odl;hpj;bat;for;f90" +# Begin Source File + +SOURCE=.\mod_lbmethod_heartbeat.c +# End Source File +# End Group +# Begin Group "Header Files" + +# PROP Default_Filter ".h" +# Begin Source File + +SOURCE=..\mod_proxy.h +# End Source File +# End Group +# Begin Source File + +SOURCE=..\..\..\build\win32\httpd.rc +# End Source File +# End Target +# End Project -- cgit v1.2.3