diff options
author | Jean-Frederic Clere <jfclere@apache.org> | 2006-07-28 16:33:58 +0000 |
---|---|---|
committer | Jean-Frederic Clere <jfclere@apache.org> | 2006-07-28 16:33:58 +0000 |
commit | d0e5132941f93827c7034fd96ae4dd49ca77c705 (patch) | |
tree | b73a323a312bc814daf0b4c59fce7c72f6c0f54d | |
parent | b09239fa4abba869569a21d3cb909eea607affb6 (diff) | |
download | httpd-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.m4 | 3 | ||||
-rw-r--r-- | modules/proxy/health_checker_util.c | 347 | ||||
-rw-r--r-- | modules/proxy/mod_proxy.c | 13 | ||||
-rw-r--r-- | modules/proxy/mod_proxy.h | 2 | ||||
-rw-r--r-- | modules/proxy/mod_proxy_health_checker.c | 73 | ||||
-rw-r--r-- | modules/proxy/mod_proxy_health_checker.h | 98 | ||||
-rw-r--r-- | modules/proxy/proxy_util.c | 21 | ||||
-rw-r--r-- | support/Makefile.in | 16 | ||||
-rw-r--r-- | support/proxymonitor.c | 304 |
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; +} |