summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorJean-Frederic Clere <jfclere@apache.org>2006-07-28 16:33:58 +0000
committerJean-Frederic Clere <jfclere@apache.org>2006-07-28 16:33:58 +0000
commitd0e5132941f93827c7034fd96ae4dd49ca77c705 (patch)
treeb73a323a312bc814daf0b4c59fce7c72f6c0f54d
parentb09239fa4abba869569a21d3cb909eea607affb6 (diff)
downloadhttpd-d0e5132941f93827c7034fd96ae4dd49ca77c705.tar.gz
First try to put togother an external health checker for mod_proxy.
git-svn-id: https://svn.apache.org/repos/asf/httpd/httpd/branches/httpd-proxy-scoreboard@426604 13f79535-47bb-0310-9956-ffa450edef68
-rw-r--r--modules/proxy/config.m43
-rw-r--r--modules/proxy/health_checker_util.c347
-rw-r--r--modules/proxy/mod_proxy.c13
-rw-r--r--modules/proxy/mod_proxy.h2
-rw-r--r--modules/proxy/mod_proxy_health_checker.c73
-rw-r--r--modules/proxy/mod_proxy_health_checker.h98
-rw-r--r--modules/proxy/proxy_util.c21
-rw-r--r--support/Makefile.in16
-rw-r--r--support/proxymonitor.c304
9 files changed, 875 insertions, 2 deletions
diff --git a/modules/proxy/config.m4 b/modules/proxy/config.m4
index 7746b09569..f2649e6ebd 100644
--- a/modules/proxy/config.m4
+++ b/modules/proxy/config.m4
@@ -17,6 +17,7 @@ proxy_connect_objs="mod_proxy_connect.lo"
proxy_ftp_objs="mod_proxy_ftp.lo"
proxy_http_objs="mod_proxy_http.lo"
proxy_fcgi_objs="mod_proxy_fcgi.lo"
+proxy_health_checker_objs="mod_proxy_health_checker.lo health_checker_util.lo"
proxy_ajp_objs="mod_proxy_ajp.lo ajp_header.lo ajp_link.lo ajp_msg.lo"
proxy_balancer_objs="mod_proxy_balancer.lo"
@@ -28,6 +29,7 @@ case "$host" in
proxy_ftp_objs="$proxy_ftp_objs mod_proxy.la"
proxy_http_objs="$proxy_http_objs mod_proxy.la"
proxy_fcgi_objs="$proxy_fcgi_objs mod_proxy.la"
+ proxy_health_checker_objs="$proxy_health_checker_objs mod_proxy.la"
proxy_ajp_objs="$proxy_ajp_objs mod_proxy.la"
proxy_balancer_objs="$proxy_balancer_objs mod_proxy.la"
;;
@@ -37,6 +39,7 @@ APACHE_MODULE(proxy_connect, Apache proxy CONNECT module, $proxy_connect_objs, ,
APACHE_MODULE(proxy_ftp, Apache proxy FTP module, $proxy_ftp_objs, , $proxy_mods_enable)
APACHE_MODULE(proxy_http, Apache proxy HTTP module, $proxy_http_objs, , $proxy_mods_enable)
APACHE_MODULE(proxy_fcgi, Apache proxy FastCGI module, $proxy_fcgi_objs, , $proxy_mods_enable)
+APACHE_MODULE(proxy_health_checker, Apache proxy health checker module, $proxy_health_checker_objs, , $proxy_mods_enable)
APACHE_MODULE(proxy_ajp, Apache proxy AJP module, $proxy_ajp_objs, , $proxy_mods_enable)
APACHE_MODULE(proxy_balancer, Apache proxy BALANCER module, $proxy_balancer_objs, , $proxy_mods_enable)
diff --git a/modules/proxy/health_checker_util.c b/modules/proxy/health_checker_util.c
new file mode 100644
index 0000000000..b36cf6c471
--- /dev/null
+++ b/modules/proxy/health_checker_util.c
@@ -0,0 +1,347 @@
+/* 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.
+ */
+
+
+/*
+ * Internal routine of the default httpd part of a health checker
+ */
+#define CORE_PRIVATE
+
+#include "apr.h"
+#include "apr_pools.h"
+
+#include "httpd.h"
+#include "http_config.h"
+#include "http_log.h"
+
+#include "mod_proxy.h"
+#include "slotmem.h"
+#include "mod_proxy_health_checker.h"
+
+#include "ajp.h"
+
+static const slotmem_storage_method *checkstorage = NULL;
+static ap_slotmem_t *myscore=NULL;
+
+/* Check a AJP back-end server.
+ * Send a cing message and wait for the answer
+ */
+static apr_status_t pingc_backend(apr_socket_t *sock, apr_pool_t *pool)
+{
+ ajp_msg_t *msg;
+ apr_status_t rc;
+ apr_byte_t result;
+
+ rc = ajp_msg_create(pool, &msg);
+ if (rc != APR_SUCCESS)
+ return rc;
+ ajp_msg_serialize_cping(msg);
+ rc = ajp_ilink_send(sock, msg);
+ if (rc != APR_SUCCESS)
+ return rc;
+ ajp_msg_reuse(msg);
+ rc = ajp_ilink_receive(sock, msg);
+ if (rc != APR_SUCCESS)
+ return rc;
+ rc = ajp_msg_peek_uint8(msg, &result);
+ if (rc != APR_SUCCESS)
+ return rc;
+ return APR_SUCCESS;
+}
+
+/*
+ * Build a connection to the backend server and check it
+ */
+static apr_status_t test_backend(char *scheme, char *hostname, int port, apr_pool_t *pool)
+{
+ apr_socket_t *newsock;
+ apr_sockaddr_t *epsv_addr;
+ apr_status_t rv;
+
+ if (!port) {
+ if (strcmp(scheme, "ajp") == 0)
+ port = 8009;
+ else if (strcmp(scheme, "http") == 0)
+ port = 80;
+ else
+ port = 443;
+ }
+ rv = apr_socket_create(&newsock, APR_INET, SOCK_STREAM, APR_PROTO_TCP, pool);
+ if (rv != APR_SUCCESS) {
+ ap_log_error(APLOG_MARK, APLOG_ERR, 0, NULL,
+ "apr_socket_create failed");
+ return rv;
+ }
+ rv = apr_sockaddr_info_get(&epsv_addr, hostname, APR_INET, port, 0, pool);
+ if (rv != APR_SUCCESS) {
+ ap_log_error(APLOG_MARK, APLOG_ERR, 0, NULL,
+ "apr_sockaddr_info_get failed");
+ apr_socket_close(newsock);
+ return rv;
+ }
+
+ rv = apr_socket_timeout_set(newsock, 10);
+ if (rv != APR_SUCCESS) {
+ ap_log_error(APLOG_MARK, APLOG_WARNING, 0, NULL,
+ "apr_socket_timeout_set");
+ apr_socket_close(newsock);
+ return rv;
+ }
+ rv = apr_socket_connect(newsock, epsv_addr);
+ if (rv != APR_SUCCESS) {
+ ap_log_error(APLOG_MARK, APLOG_DEBUG, 0, NULL,
+ "apr_socket_connect failed");
+ apr_socket_close(newsock);
+ return rv;
+ }
+
+ /* XXX: Something is needed for http/https */
+ if (strcmp(scheme, "ajp") == 0) {
+ /* The connection is etablished send a ping and read the answer */
+ apr_socket_timeout_set(newsock, 10000);
+ rv = pingc_backend(newsock, pool);
+ if (rv != APR_SUCCESS) {
+ ap_log_error(APLOG_MARK, APLOG_DEBUG, 0, NULL,
+ "pingc_backend failed");
+ apr_socket_close(newsock);
+ return rv;
+ }
+ }
+ apr_socket_close(newsock);
+ return APR_SUCCESS;
+}
+
+/* read the size of the entry: to create the shared area */
+static int getentrysize()
+{
+ return sizeof(struct proxy_worker_conf);
+}
+/* copy the worker information in the shared area so the health-checker can extract the part it need */
+static apr_status_t add_entry(proxy_worker *worker, char *balancer_name, int id)
+{
+ struct proxy_worker_conf *workerconf = NULL;
+ apr_status_t rv;
+
+ if (myscore == NULL)
+ return APR_ENOSHMAVAIL;
+ rv = checkstorage->ap_slotmem_mem(myscore, worker->id, (void *) &workerconf);
+ if (rv != APR_SUCCESS) {
+ return rv;
+ }
+
+ if (balancer_name)
+ strcpy(workerconf->balancer_name, balancer_name);
+ workerconf->id = worker->id;
+ workerconf->retry = worker->retry;
+ workerconf->lbfactor = worker->lbfactor;
+ if (worker->name)
+ strcpy(workerconf->name, worker->name);
+ if (worker->scheme)
+ strcpy(workerconf->scheme, worker->scheme);
+ if (worker->hostname)
+ strcpy(workerconf->hostname, worker->hostname);
+ if (worker->route)
+ strcpy(workerconf->route, worker->route);
+ if (worker->redirect)
+ strcpy(workerconf->redirect, worker->redirect);
+ workerconf->status = worker->status;
+ workerconf->port = worker->port;
+ workerconf->min = worker->min;
+ workerconf->smax = worker->smax;
+ workerconf->hmax = worker->hmax;
+ workerconf->ttl = worker->ttl;
+ workerconf->timeout = worker->timeout;
+ workerconf->acquire = worker->acquire;
+ workerconf->acquire_set = worker->acquire_set;
+ workerconf->recv_buffer_size = worker->recv_buffer_size;
+ workerconf->recv_buffer_size_set = worker->recv_buffer_size_set;
+ workerconf->io_buffer_size = worker->io_buffer_size;
+ workerconf->io_buffer_size_set = worker->io_buffer_size_set;
+ workerconf->keepalive = worker->keepalive;
+ workerconf->keepalive_set = worker->keepalive_set;
+ workerconf->flush_packets = worker->flush_packets;
+ workerconf->flush_wait = worker->flush_wait;
+ workerconf->health = 0;
+ workerconf->used = 1;
+ return APR_SUCCESS;
+}
+/* Remove the entry: TO BE DONE */
+static apr_status_t del_entry(int id)
+{
+ return APR_SUCCESS;
+}
+/* read the health of the entry: for httpd */
+static apr_status_t get_health(int id, int *health)
+{
+ struct proxy_worker_conf *workerconf = NULL;
+ apr_status_t rv;
+
+ if (myscore == NULL)
+ return APR_ENOSHMAVAIL;
+ rv = checkstorage->ap_slotmem_mem(myscore, id, (void *) &workerconf);
+ if (rv != APR_SUCCESS)
+ return rv;
+ *health = workerconf->health;
+ return APR_SUCCESS;
+}
+/* set the health of the entry: for the health-checker */
+static apr_status_t set_health(int id, int value)
+{
+ struct proxy_worker_conf *workerconf = NULL;
+ apr_status_t rv;
+
+ if (myscore == NULL)
+ return APR_ENOSHMAVAIL;
+ rv = checkstorage->ap_slotmem_mem(myscore, id, (void *) &workerconf);
+ if (rv != APR_SUCCESS)
+ return rv;
+ workerconf->health = value;
+ workerconf->time_checked = apr_time_now();
+ return APR_SUCCESS;
+}
+/* read the entry stored in the shared area and build the corresponding worker structure */
+static apr_status_t get_entry(int id, proxy_worker **worker, char **balancer_name, apr_pool_t *pool)
+{
+ struct proxy_worker_conf *workerconf = NULL;
+ apr_status_t rv;
+
+ if (myscore == NULL)
+ return APR_ENOSHMAVAIL;
+ rv = checkstorage->ap_slotmem_mem(myscore, id, (void *) &workerconf);
+ if (rv != APR_SUCCESS)
+ return rv;
+
+ /* allocate the data */
+ *worker = apr_pcalloc(pool, sizeof(proxy_worker));
+ if (workerconf->balancer_name)
+ *balancer_name = apr_pcalloc(pool, strlen(workerconf->balancer_name));
+ else
+ *balancer_name = NULL;
+
+ /* The httpstatus is handle by httpd don't touch it here */
+ (* worker)->id = workerconf->id;
+ // XXX: what to do (* worker)->s = workerconf;
+ (* worker)->retry = workerconf->retry;
+ (* worker)->lbfactor = workerconf->lbfactor;
+ if (workerconf->name)
+ strcpy((* worker)->name, workerconf->name);
+ if (workerconf->scheme)
+ strcpy((* worker)->scheme, workerconf->scheme);
+ if (workerconf->hostname)
+ strcpy((* worker)->hostname, workerconf->hostname);
+ if (workerconf->route)
+ strcpy((* worker)->route, workerconf->route);
+ if (workerconf->redirect)
+ strcpy((* worker)->redirect, workerconf->redirect);
+ (* worker)->status = workerconf->status;
+ (* worker)->port = workerconf->port;
+ (* worker)->min = workerconf->min;
+ (* worker)->smax = workerconf->smax;
+ (* worker)->hmax = workerconf->hmax;
+ (* worker)->ttl = workerconf->ttl;
+ (* worker)->timeout = workerconf->timeout;
+ (* worker)->acquire = workerconf->acquire;
+ (* worker)->acquire_set = workerconf->acquire_set;
+ (* worker)->recv_buffer_size = workerconf->recv_buffer_size;
+ (* worker)->recv_buffer_size_set = workerconf->recv_buffer_size_set;
+ (* worker)->io_buffer_size = workerconf->io_buffer_size;
+ (* worker)->io_buffer_size_set = workerconf->io_buffer_size_set;
+ (* worker)->keepalive = workerconf->keepalive;
+ (* worker)->keepalive_set = workerconf->keepalive_set;
+ (* worker)->flush_packets = workerconf->flush_packets;
+ (* worker)->flush_wait = workerconf->flush_wait;
+ return APR_SUCCESS;
+}
+/* read the entry stored in the shared area */
+static apr_status_t get_entryconf(int id, struct proxy_worker_conf **workerconf, char **balancer_name, apr_pool_t *pool)
+{
+ apr_status_t rv;
+
+ if (myscore == NULL)
+ return APR_ENOSHMAVAIL;
+ rv = checkstorage->ap_slotmem_mem(myscore, id, workerconf);
+ if (rv != APR_SUCCESS)
+ return rv;
+ *balancer_name = (*workerconf)->balancer_name;
+ return APR_SUCCESS;
+}
+
+/* Test the corresponding back-end server */
+static apr_status_t check_entryhealth(int id, apr_pool_t *pool) {
+ apr_status_t rv;
+ struct proxy_worker_conf *workerconf;
+
+ if (myscore == NULL)
+ return APR_ENOSHMAVAIL;
+ rv = checkstorage->ap_slotmem_mem(myscore, id, &workerconf);
+ if (rv != APR_SUCCESS)
+ return rv;
+ /* If the error is not initialized to the worker to be removed keep it */
+ if (workerconf->used != VALID)
+ return APR_SUCCESS;
+ rv = test_backend(workerconf->scheme, workerconf->hostname, workerconf->port, pool);
+ if (rv != APR_SUCCESS)
+ workerconf->health = HEALTH_NO;
+ else
+ workerconf->health = HEALTH_OK;
+ workerconf->time_checked = apr_time_now();
+ return rv;
+}
+
+/* check the connection pool used by the worker */
+static apr_status_t check_poolhealth(proxy_worker *worker, int id, apr_pool_t *pool)
+{
+ /* XXX: The code is missing */
+ return APR_SUCCESS;
+}
+
+/* The stuff we provide */
+static const health_worker_method worker_storage = {
+ &getentrysize,
+ &add_entry,
+ &del_entry,
+ &get_health,
+ &set_health,
+ &get_entry,
+ &get_entryconf,
+ &check_entryhealth
+};
+
+/* make the module usuable from outside */
+health_worker_method *health_checker_get_storage()
+{
+ return(&worker_storage);
+}
+
+/* handle the slotmem storage */
+void health_checker_init_slotmem_storage(slotmem_storage_method * storage)
+{
+ checkstorage = storage;
+}
+slotmem_storage_method * health_checker_get_slotmem_storage()
+{
+ return(checkstorage);
+}
+
+/* handle the slotmen itself */
+void health_checker_init_slotmem(ap_slotmem_t *score)
+{
+ myscore = score;
+}
+ap_slotmem_t *health_checker_get_slotmem()
+{
+ return(myscore);
+}
diff --git a/modules/proxy/mod_proxy.c b/modules/proxy/mod_proxy.c
index af87ac8aae..70d6b77773 100644
--- a/modules/proxy/mod_proxy.c
+++ b/modules/proxy/mod_proxy.c
@@ -1151,11 +1151,13 @@ static const char *
}
}
else {
+ int adding = 0;
proxy_worker *worker = ap_proxy_get_worker(cmd->temp_pool, conf, r);
if (!worker) {
const char *err = ap_proxy_add_worker(&worker, cmd->pool, conf, r);
if (err)
return apr_pstrcat(cmd->temp_pool, "ProxyPass ", err, NULL);
+ adding = 1;
} else {
ap_log_error(APLOG_MARK, APLOG_WARNING, 0, cmd->server,
"worker %s already used by another worker", worker->name);
@@ -1168,6 +1170,10 @@ static const char *
if (err)
return apr_pstrcat(cmd->temp_pool, "ProxyPass ", err, NULL);
}
+
+ /* XXX: ProxyPass is not a good name look for Location? */
+ if (adding)
+ proxy_checkstorage_add_entry(worker, "ProxyPass");
}
return NULL;
}
@@ -1516,7 +1522,8 @@ static const char *add_member(cmd_parms *cmd, void *dummy, const char *arg)
apr_table_t *params = apr_table_make(cmd->pool, 5);
const apr_array_header_t *arr;
const apr_table_entry_t *elts;
- int i;
+ int i;
+ int adding = 0;
if (cmd->path)
path = apr_pstrdup(cmd->pool, cmd->path);
@@ -1552,6 +1559,7 @@ static const char *add_member(cmd_parms *cmd, void *dummy, const char *arg)
const char *err;
if ((err = ap_proxy_add_worker(&worker, cmd->pool, conf, name)) != NULL)
return apr_pstrcat(cmd->temp_pool, "BalancerMember ", err, NULL);
+ adding = 1;
} else {
ap_log_error(APLOG_MARK, APLOG_WARNING, 0, cmd->server,
"worker %s already used by another worker", worker->name);
@@ -1577,6 +1585,9 @@ static const char *add_member(cmd_parms *cmd, void *dummy, const char *arg)
}
/* Add the worker to the load balancer */
ap_proxy_add_worker_to_balancer(cmd->pool, balancer, worker);
+ /* XXX: Holy cow: The worker can belong to more that one balancer! */
+ if (adding)
+ proxy_checkstorage_add_entry(worker, balancer->name);
return NULL;
}
diff --git a/modules/proxy/mod_proxy.h b/modules/proxy/mod_proxy.h
index bc35f87e79..36c9ef8468 100644
--- a/modules/proxy/mod_proxy.h
+++ b/modules/proxy/mod_proxy.h
@@ -127,6 +127,7 @@ typedef struct proxy_balancer proxy_balancer;
typedef struct proxy_worker proxy_worker;
typedef struct proxy_conn_pool proxy_conn_pool;
typedef struct proxy_balancer_method proxy_balancer_method;
+typedef struct health_worker_method health_worker_method;
typedef struct {
apr_array_header_t *proxies;
@@ -723,6 +724,7 @@ PROXY_DECLARE(void) ap_proxy_backend_broke(request_rec *r,
#endif
#define PROXY_LBMETHOD "proxylbmethod"
+#define PROXY_CKMETHOD "proxyckmethod"
/* The number of dynamic workers that can be added when reconfiguring.
* If this limit is reached you must stop and restart the server.
diff --git a/modules/proxy/mod_proxy_health_checker.c b/modules/proxy/mod_proxy_health_checker.c
new file mode 100644
index 0000000000..56655229ac
--- /dev/null
+++ b/modules/proxy/mod_proxy_health_checker.c
@@ -0,0 +1,73 @@
+/*
+ * Default httpd part of the health checker
+ */
+#define CORE_PRIVATE
+
+#include "apr.h"
+#include "apr_pools.h"
+
+#include "httpd.h"
+#include "http_config.h"
+#include "http_log.h"
+
+#include "mod_proxy.h"
+#include "slotmem.h"
+#include "mod_proxy_health_checker.h"
+
+static int healthck_pre_config(apr_pool_t *pconf, apr_pool_t *plog,
+ apr_pool_t *ptemp)
+{
+ slotmem_storage_method *checkstorage;
+ health_worker_method *worker_storage = health_checker_get_storage();
+ ap_slotmem_t *myscore;
+
+ checkstorage = ap_lookup_provider(SLOTMEM_STORAGE, "shared", "0");
+ if (checkstorage) {
+ health_checker_init_slotmem_storage(checkstorage);
+ }
+ if (checkstorage && worker_storage) {
+ checkstorage->ap_slotmem_create(&myscore, "proxy/checker", worker_storage->getentrysize(), 128, pconf);
+ health_checker_init_slotmem(myscore);
+ }
+ return OK;
+}
+
+/* XXX: Was to get ap_proxy_lb_workers()
+static int healthck_post_config(apr_pool_t *pconf, apr_pool_t *plog,
+ apr_pool_t *ptemp, server_rec *s)
+{
+ slotmem_storage_method *checkstorage = health_checker_get_slotmem_storage();
+ health_worker_method *worker_storage = health_checker_get_storage();
+ ap_slotmem_t *myscore;
+
+ if (checkstorage && worker_storage) {
+ checkstorage->ap_slotmem_create(&myscore, "proxy/checker", worker_storage->getentrysize(), ap_proxy_lb_workers(), pconf);
+ health_checker_init_slotmem(myscore);
+ }
+ return OK;
+
+}
+ */
+
+static void ap_healthstore_register_hook(apr_pool_t *p)
+{
+ static const char * const aszPre[] = { "mod_proxy.c", NULL };
+ static const char * const aszPos[] = { "mod_sharedmem.c", NULL };
+
+ health_worker_method *worker_storage = health_checker_get_storage();
+ ap_register_provider(p, PROXY_CKMETHOD, "default", "0", worker_storage);
+ ap_hook_pre_config(healthck_pre_config, NULL, aszPos, APR_HOOK_MIDDLE);
+ /* XXX: Too late....
+ ap_hook_post_config(healthck_post_config, aszPre, NULL, APR_HOOK_MIDDLE);
+ */
+}
+
+module AP_MODULE_DECLARE_DATA proxy_health_checker_module = {
+ 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 */
+ ap_healthstore_register_hook /* register hooks */
+};
diff --git a/modules/proxy/mod_proxy_health_checker.h b/modules/proxy/mod_proxy_health_checker.h
new file mode 100644
index 0000000000..a4ec9ba3bf
--- /dev/null
+++ b/modules/proxy/mod_proxy_health_checker.h
@@ -0,0 +1,98 @@
+/* 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.
+ */
+
+/* health checker routines for proxies */
+#define HEALTH_OK 1
+#define HEALTH_NO 2
+#define HEALTH_UNKNOWN 0
+
+/* Validity of the entry */
+#define VALID 1
+#define REMOVED 2
+#define UNINITIALIZED 0
+
+typedef struct proxy_worker_conf proxy_worker_conf;
+
+/* allow health check method on workers in a non httpd process */
+struct health_worker_method {
+ /* read the size of the entry: to create the shared area */
+ int (* getentrysize)();
+ /* copy the worker information in the shared area so the health-checker can extract the part it need */
+ apr_status_t (*add_entry)(proxy_worker *worker, char *balancer_name, int id);
+ /* XXX : Remove the entry */
+ apr_status_t (*del_entry)(int id);
+ /* read the health of the entry: for httpd */
+ apr_status_t (*get_health)(int id, int *health);
+ /* set the health of the entry: for the health-checker */
+ apr_status_t (*set_health)(int id, int value);
+ /* read the entry stored in the shared area */
+ apr_status_t (*get_entry)(proxy_worker **worker, char **balancer_name, apr_pool_t *pool);
+ /* read the conf part. */
+ apr_status_t (*get_entryconf)(int id, proxy_worker_conf **worker, char **balancer_name, apr_pool_t *pool);
+ /* check the back-end server health */
+ apr_status_t (*check_entryhealth)(int id, apr_pool_t *pool);
+ /* check the pool of sockets (are they still connected) */
+ apr_status_t (*check_poolhealth)(int id, proxy_worker *worker, apr_pool_t *pool);
+};
+
+/* To store the configuration of the balancers and workers.
+ */
+struct proxy_balancer_conf {
+ char name[32];
+ char sticky[32];
+ int sticky_force;
+ apr_interval_time_t timeout;
+ int max_attempts;
+ char max_attempts_set;
+ char lbmethod_name[32];
+};
+
+struct proxy_worker_conf {
+ proxy_worker_stat httpstatus; /* httpd private */
+ char balancer_name[32];
+ int id; /* scoreboard id */
+ apr_interval_time_t retry; /* retry interval */
+ int lbfactor; /* initial load balancing factor */
+ char name[64];
+ char scheme[6]; /* scheme to use ajp|http|https */
+ char hostname[64]; /* remote backend address */
+ char route[128]; /* balancing route */
+ char redirect[128]; /* temporary balancing redirection route */
+ int status; /* temporary worker status */
+ apr_port_t port;
+ int min; /* Desired minimum number of available connections */
+ int smax; /* Soft maximum on the total number of connections */
+ int hmax; /* Hard maximum on the total number of connections */
+ apr_interval_time_t ttl; /* maximum amount of time in seconds a connection
+ * may be available while exceeding the soft limit */
+ apr_interval_time_t timeout; /* connection timeout */
+ char timeout_set;
+ apr_interval_time_t acquire; /* acquire timeout when the maximum number of connections is exceeded */
+ char acquire_set;
+ apr_size_t recv_buffer_size;
+ char recv_buffer_size_set;
+ apr_size_t io_buffer_size;
+ char io_buffer_size_set;
+ char keepalive;
+ char keepalive_set;
+ int is_address_reusable;
+ int flush_packets;
+ int flush_wait; /* poll wait time in microseconds if flush_auto */
+ int health;
+ int used; /* 1 : valid entry 2 : remove 0 : free slot */
+ apr_time_t time_checked;
+};
+
diff --git a/modules/proxy/proxy_util.c b/modules/proxy/proxy_util.c
index 8f12d25e57..2fed720313 100644
--- a/modules/proxy/proxy_util.c
+++ b/modules/proxy/proxy_util.c
@@ -17,6 +17,7 @@
/* Utility routines for Apache proxy */
#include "mod_proxy.h"
#include "slotmem.h"
+#include "mod_proxy_health_checker.h"
#include "ap_mpm.h"
#include "apr_version.h"
@@ -43,6 +44,8 @@ APR_IMPLEMENT_OPTIONAL_HOOK_RUN_ALL(proxy, PROXY, int, create_req,
OK, DECLINED)
/* Storage for the comarea */
static const slotmem_storage_method *storage = NULL;
+/* Health checker handler */
+static const health_worker_method *checkstorage = NULL;
/* already called in the knowledge that the characters are hex digits */
PROXY_DECLARE(int) ap_proxy_hex2c(const char *x)
@@ -1639,6 +1642,13 @@ PROXY_DECLARE(void) ap_proxy_initialize_worker_share(proxy_server_conf *conf,
worker->name);
return;
}
+
+ /* Health checker handler: to create the correct size. */
+ if (checkstorage) {
+ item_size = checkstorage->getentrysize();
+ }
+
+ /* Use storage provider when a storage is existing */
if (storage) {
rv = storage->ap_slotmem_create(&myscore, "proxy/comarea", item_size, ap_proxy_lb_workers(), conf->pool);
@@ -2224,6 +2234,8 @@ PROXY_DECLARE(void) proxy_create_comarea(apr_pool_t *pconf)
{
ap_slotmem_t *myscore;
apr_size_t item_size = sizeof(proxy_worker_stat);
+ if (checkstorage)
+ item_size = checkstorage->getentrysize();
if (storage)
storage->ap_slotmem_create(&myscore, "proxy/comarea", item_size, ap_proxy_lb_workers(), pconf);
}
@@ -2235,4 +2247,13 @@ PROXY_DECLARE(void) proxy_lookup_storage_provider()
storage = ap_lookup_provider(SLOTMEM_STORAGE, "score", "0");
if (!storage)
storage = ap_lookup_provider(SLOTMEM_STORAGE, "plain", "0");
+ checkstorage = ap_lookup_provider(PROXY_CKMETHOD, "default", "0");
+}
+
+/* Store the worker information in the comarea */
+PROXY_DECLARE(void) proxy_checkstorage_add_entry(proxy_worker *worker, char *balancer_name)
+{
+ if (checkstorage) {
+ checkstorage->add_entry(worker, balancer_name, worker->id);
+ }
}
diff --git a/support/Makefile.in b/support/Makefile.in
index 670e465d9d..de99d1dc0e 100644
--- a/support/Makefile.in
+++ b/support/Makefile.in
@@ -3,7 +3,7 @@ DISTCLEAN_TARGETS = apxs apachectl dbmmanage log_server_status \
CLEAN_TARGETS = suexec
-PROGRAMS = htpasswd htdigest rotatelogs logresolve ab checkgid htdbm htcacheclean httxt2dbm fcgistarter
+PROGRAMS = htpasswd htdigest rotatelogs logresolve ab checkgid htdbm htcacheclean httxt2dbm fcgistarter proxymonitor
TARGETS = $(PROGRAMS)
PROGRAM_LDADD = $(UTIL_LDFLAGS) $(PROGRAM_DEPENDENCIES) $(EXTRA_LIBS) $(AP_LIBS)
@@ -73,3 +73,17 @@ httxt2dbm: $(httxt2dbm_OBJECTS)
fcgistarter_OBJECTS = fcgistarter.lo
fcgistarter: $(fcgistarter_OBJECTS)
$(LINK) $(fcgistarter_LTFLAGS) $(fcgistarter_OBJECTS) $(PROGRAM_LDADD)
+
+#proxymonitor_OBJECTS = proxymonitor.lo ../modules/proxy/ajp_msg.lo ../modules/proxy/ajp_link.lo ../modules/proxy/health_checker_util.lo ../modules/mem/sharedmem_util.lo
+proxymonitor_OBJECTS = proxymonitor.lo ajp_msg.lo ajp_link.lo health_checker_util.lo sharedmem_util.lo
+ajp_msg.c: ../modules/proxy/ajp_msg.c
+ cp ../modules/proxy/ajp_msg.c .
+ajp_link.c: ../modules/proxy/ajp_link.c
+ cp ../modules/proxy/ajp_link.c .
+health_checker_util.c: ../modules/proxy/health_checker_util.c
+ cp ../modules/proxy/health_checker_util.c .
+sharedmem_util.c: ../modules/mem/sharedmem_util.c
+ cp ../modules/mem/sharedmem_util.c .
+
+proxymonitor: $(proxymonitor_OBJECTS)
+ $(LINK) $(proxymonitor_LTFLAGS) $(proxymonitor_OBJECTS) $(PROGRAM_LDADD)
diff --git a/support/proxymonitor.c b/support/proxymonitor.c
new file mode 100644
index 0000000000..0147b35f33
--- /dev/null
+++ b/support/proxymonitor.c
@@ -0,0 +1,304 @@
+/* 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.
+ */
+
+
+/*
+ * proxymonitor.c: simple program for monitor proxy back-end server.
+ *
+ */
+
+#include "apr.h"
+#include "apr_lib.h"
+#include "apr_strings.h"
+#include "apr_file_io.h"
+#include "apr_file_info.h"
+#include "apr_pools.h"
+#include "apr_hash.h"
+#include "apr_thread_proc.h"
+#include "apr_signal.h"
+#include "apr_getopt.h"
+#include "apr_ring.h"
+#include "apr_date.h"
+
+#include "mod_proxy.h"
+#include "ajp.h"
+
+#include "mod_proxy_health_checker.h"
+#include "slotmem.h"
+
+#if APR_HAVE_UNISTD_H
+#include <unistd.h>
+#endif
+#if APR_HAVE_STDLIB_H
+#include <stdlib.h>
+#endif
+
+static int interrupted; /* flag: true if SIGINT or SIGTERM occurred */
+
+static apr_time_t now; /* start time of this processing run */
+
+extern int AP_DECLARE_DATA ap_default_loglevel = APLOG_ERR;
+
+static apr_file_t *errfile; /* stderr file handle */
+static apr_file_t *outfile; /* stdout file handle */
+
+/* short program name as called */
+static const char *shortname = "proxymonitor";
+
+static const health_worker_method *worker_storage;
+
+char *basedir = NULL;
+
+/* XXX: hack to use a part of the mod_sharedmem and mod_proxy_health_checker */
+static apr_status_t init_healthck(apr_pool_t *pool, int *num)
+{
+ apr_size_t size;
+ apr_status_t rv;
+ slotmem_storage_method *checkstorage;
+ ap_slotmem_t *myscore;
+
+ sharedmem_initglobalpool(pool);
+ checkstorage = sharedmem_getstorage();
+ rv = checkstorage->ap_slotmem_attach(&myscore, "proxy/checker", &size, num, pool);
+
+ health_checker_init_slotmem_storage(checkstorage);
+ health_checker_init_slotmem(myscore);
+ worker_storage = health_checker_get_storage();
+
+ return rv;
+}
+
+/*
+ * httpd routine to be able to link with the modules.
+ */
+char * ap_server_root_relative(apr_pool_t *p, const char *name)
+{
+ char *fname;
+
+ /* XXX: apr_filepath_merge better ? */
+ if (basedir && name[0] != '/') {
+ fname = apr_pcalloc(p, strlen(basedir)+strlen(name)+1);
+ strcpy(fname, basedir);
+ strcat(fname, "/");
+ strcat(fname, name);
+ } else {
+ fname = apr_pstrdup(p, name);
+ }
+ return fname;
+}
+
+/*
+ * called on SIGINT or SIGTERM
+ */
+static void setterm(int unused)
+{
+ interrupted = 1;
+}
+
+/*
+ * called in out of memory condition
+ */
+static int oom(int unused)
+{
+ static int called = 0;
+
+ /* be careful to call exit() only once */
+ if (!called) {
+ called = 1;
+ exit(1);
+ }
+ return APR_ENOMEM;
+}
+
+/*
+ * usage info
+ */
+#define NL APR_EOL_STR
+static void usage(void)
+{
+ apr_file_printf(errfile,
+ "%s -- program for monitoring proxies of httpd." NL
+ "Usage: %s [-n] [-pPATH] [-dINTERVAL] [-rN]" NL
+ NL
+ "Options:" NL
+ " -d Repeat checking every INTERVAL seconds." NL
+ NL
+ " -r Repeat checking N times." NL
+ NL
+ " -p Specify PATH where the httpd is running." NL,
+
+ shortname,
+ shortname,
+ shortname
+ );
+
+ exit(1);
+}
+#undef NL
+
+/* Quick hack to allow logging */
+AP_DECLARE(void) ap_log_error(const char *file, int line, int level,
+ apr_status_t status, const server_rec *s,
+ const char *fmt, ...)
+{
+ va_list args;
+ char scratch[MAX_STRING_LEN];
+
+ va_start(args, fmt);
+ apr_vsnprintf(scratch, MAX_STRING_LEN, fmt, args);
+ apr_file_printf(errfile,"%s\n", scratch);
+ va_end(args);
+}
+
+/*
+ * Reads the configuration from shared memory
+ */
+int process_sharedmem(apr_pool_t *pool, int num)
+{
+ apr_status_t rv;
+ int n;
+ struct proxy_worker_conf *worker;
+ char *balancer_name;
+ int status;
+
+ for (n = 0; n < num; n++) {
+
+ rv = worker_storage->get_entryconf(n, &worker, &balancer_name, pool);
+ if (worker->used == 0 || worker->used == 2)
+ continue;
+ worker_storage->get_health(n, &status);
+ apr_file_printf(outfile, "balancer %s worker %s: host %s port %d status: %d ",
+ worker->balancer_name, worker->name,
+ worker->hostname, worker->port, status);
+ rv = worker_storage->check_entryhealth(n, pool);
+ if (rv != APR_SUCCESS) {
+ apr_file_printf(outfile, "now: FAILED\n");
+ worker_storage->set_health(n, HEALTH_NO);
+ } else {
+ apr_file_printf(outfile, "now: OK\n");
+ worker_storage->set_health(n, HEALTH_OK);
+ }
+ }
+}
+
+/*
+ * main
+ */
+int main(int argc, const char * const argv[])
+{
+ apr_time_t current, delay;
+ apr_status_t status;
+ apr_pool_t *pool, *instance, *instance_socket;
+ apr_getopt_t *o;
+ int repeat = -1;
+ char opt;
+ const char *arg;
+ char datestring[APR_RFC822_DATE_LEN];
+ int num;
+
+ /* only log errors */
+ // ap_default_loglevel = APLOG_ERR;
+
+ delay = 5 * APR_USEC_PER_SEC;
+
+ if (apr_app_initialize(&argc, &argv, NULL) != APR_SUCCESS) {
+ return 1;
+ }
+ atexit(apr_terminate);
+
+ if (argc) {
+ shortname = apr_filepath_name_get(argv[0]);
+ }
+
+ if (apr_pool_create(&pool, NULL) != APR_SUCCESS) {
+ return 1;
+ }
+ apr_pool_abort_set(oom, pool);
+ apr_file_open_stderr(&errfile, pool);
+ apr_file_open_stdout(&outfile, pool);
+ apr_signal(SIGINT, setterm);
+ apr_signal(SIGTERM, setterm);
+
+ apr_getopt_init(&o, pool, argc, argv);
+
+ while (1) {
+ status = apr_getopt(o, "p:d:r:", &opt, &arg);
+ if (status == APR_EOF) {
+ break;
+ }
+ else if (status != APR_SUCCESS) {
+ usage();
+ }
+ else {
+ switch (opt) {
+ case 'd':
+ delay = apr_atoi64(arg);
+ delay *= APR_USEC_PER_SEC;
+ break;
+
+
+ case 'r':
+ repeat = apr_atoi64(arg);
+ break;
+
+ case 'p':
+ if (basedir) {
+ usage();
+ }
+ basedir = apr_pstrdup(pool, arg);
+ break;
+ default:
+ usage();
+ } /* switch */
+ } /* else */
+ } /* while */
+ if (basedir == NULL)
+ usage();
+
+ instance_socket = NULL;
+
+ while (repeat && ! interrupted) {
+
+ if (instance_socket == NULL) {
+ apr_pool_create(&instance_socket, pool);
+ init_healthck(instance_socket, &num);
+ }
+
+ apr_pool_create(&instance, instance_socket);
+ apr_sleep(delay);
+ now = apr_time_now();
+ process_sharedmem(instance_socket, num);
+ current = apr_time_now();
+ apr_rfc822_date(datestring, current);
+ apr_file_printf(outfile,"at %s in %d\n", datestring, current-now);
+
+ if (repeat>0)
+ repeat--;
+ apr_pool_destroy(instance);
+ /* If something goes really wrong we should clean all */
+ if (0) {
+ apr_pool_destroy(instance_socket);
+ instance_socket = NULL;
+ }
+ }
+ if (interrupted) {
+ apr_file_printf(errfile, "Monitoring aborted due to user "
+ "request." APR_EOL_STR);
+ return 1;
+ }
+
+ return 0;
+}