#include "ap_mpm.h"
#include "apr_version.h"
#include "apr_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
module AP_MODULE_DECLARE_DATA lbmethod_heartbeat_module;
+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;
const char *ip;
int busy;
int ready;
- int seen;
+ 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)
{
}
}
-static apr_status_t read_heartbeats(const char *path, apr_hash_t *servers,
+static apr_status_t readfile_heartbeats(const char *path, apr_hash_t *servers,
apr_pool_t *pool)
{
apr_finfo_t fi;
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);
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,
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;
+}
+
/*
* Finding a random number in a range.
* n' = a + n(b-a+1)/(M+1)
apr_status_t rv;
int i;
apr_uint32_t openslots = 0;
- proxy_worker *worker;
+ proxy_worker **worker;
hb_server_t *server;
apr_array_header_t *up_servers;
proxy_worker *mycandidate = 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->hostname, APR_HASH_KEY_STRING);
+ 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,
+ "lb_heartbeat: No server for worker %s", (*worker)->s->name);
continue;
}
- if (!PROXY_WORKER_IS_USABLE(worker)) {
- ap_proxy_retry_worker("BALANCER", worker, r->server);
+ if (!PROXY_WORKER_IS_USABLE(*worker)) {
+ ap_proxy_retry_worker("BALANCER", *worker, r->server);
}
- if (PROXY_WORKER_IS_USABLE(worker)) {
- server->worker = worker;
+ 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;;
+ apr_uint32_t pick = 0;
rv = random_pick(&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) {
+ if (pick >= c && pick <= c + server->ready) {
mycandidate = server->worker;
}
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
+ 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, "shared", "0");
+ if (!storage) {
+ ap_log_error(APLOG_MARK, APLOG_NOERRNO|APLOG_NOTICE, 0, s, "ap_lookup_provider %s failed", 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_NOERRNO|APLOG_NOTICE, 0, s, "No slotmem from mod_heartmonitor");
+ } else
+ ap_log_error(APLOG_MARK, APLOG_NOERRNO|APLOG_NOTICE, 0, s, "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)
{NULL}
};
-module AP_MODULE_DECLARE_DATA lbmethod_heartbeat_module = {
+AP_DECLARE_MODULE(lbmethod_heartbeat) = {
STANDARD20_MODULE_STUFF,
NULL, /* create per-directory config structure */
NULL, /* merge per-directory config structures */