]> git.ipfire.org Git - thirdparty/apache/httpd.git/commitdiff
First try to put togother an external health checker for mod_proxy.
authorJean-Frederic Clere <jfclere@apache.org>
Fri, 28 Jul 2006 16:33:58 +0000 (16:33 +0000)
committerJean-Frederic Clere <jfclere@apache.org>
Fri, 28 Jul 2006 16:33:58 +0000 (16:33 +0000)
git-svn-id: https://svn.apache.org/repos/asf/httpd/httpd/branches/httpd-proxy-scoreboard@426604 13f79535-47bb-0310-9956-ffa450edef68

modules/proxy/config.m4
modules/proxy/health_checker_util.c [new file with mode: 0644]
modules/proxy/mod_proxy.c
modules/proxy/mod_proxy.h
modules/proxy/mod_proxy_health_checker.c [new file with mode: 0644]
modules/proxy/mod_proxy_health_checker.h [new file with mode: 0644]
modules/proxy/proxy_util.c
support/Makefile.in
support/proxymonitor.c [new file with mode: 0644]

index 7746b09569fd87ebdc24240e170a4f2584d1c989..f2649e6ebd98c9acc4f5c5535d13243dbf6812b5 100644 (file)
@@ -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 (file)
index 0000000..b36cf6c
--- /dev/null
@@ -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);
+}
index af87ac8aae6a03130c9191750b41e5427198e237..70d6b77773829da165746fe2568e4b58cf7f8e3c 100644 (file)
@@ -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;
 }
 
index bc35f87e7988205ebd666c6e7d4c6d55d677a502..36c9ef846877ab427fc008abdef19243caa057bb 100644 (file)
@@ -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 (file)
index 0000000..5665522
--- /dev/null
@@ -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 (file)
index 0000000..a4ec9ba
--- /dev/null
@@ -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;
+};
+
index 8f12d25e57ea45f59e66cf587a517fa9e2eb57f0..2fed7203136796cb22fe06e2b8a36f6e8513cc58 100644 (file)
@@ -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);
+    }
 }
index 670e465d9dc69c293df55de4a867883ac011691c..de99d1dc0e08d2903a224c89cd6b78c7666ac603 100644 (file)
@@ -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 (file)
index 0000000..0147b35
--- /dev/null
@@ -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;
+}