1
0
mirror of https://github.com/apache/httpd.git synced 2025-08-08 15:02:10 +03:00

Commit framework impl of health-check module plus

required changes. The actual health checking is
currently in progress, but wanted to add in at
this stage.

git-svn-id: https://svn.apache.org/repos/asf/httpd/httpd/trunk@1722177 13f79535-47bb-0310-9956-ffa450edef68
This commit is contained in:
Jim Jagielski
2015-12-29 16:12:04 +00:00
parent cdb2aea61f
commit 33aceece57
4 changed files with 308 additions and 2 deletions

View File

@@ -10,6 +10,10 @@ else
proxy_mods_enable=most proxy_mods_enable=most
fi fi
if test "$proxy_mods_enable" = "no"; then
enable_proxy_hcheck=no
fi
proxy_objs="mod_proxy.lo proxy_util.lo" proxy_objs="mod_proxy.lo proxy_util.lo"
APACHE_MODULE(proxy, Apache proxy module, $proxy_objs, , $proxy_mods_enable) APACHE_MODULE(proxy, Apache proxy module, $proxy_objs, , $proxy_mods_enable)
@@ -68,6 +72,7 @@ APACHE_MODULE(serf, [Reverse proxy module using Serf], , , no, [
]) ])
APACHE_MODULE(proxy_express, mass reverse-proxy module. Requires --enable-proxy., , , $proxy_mods_enable,, proxy) APACHE_MODULE(proxy_express, mass reverse-proxy module. Requires --enable-proxy., , , $proxy_mods_enable,, proxy)
APACHE_MODULE(proxy_hcheck, reverse-proxy health-check module. Requires --enable-proxy and --enable-watchdog., , , $enable_proxy_hcheck,, watchdog)
APR_ADDTO(INCLUDES, [-I\$(top_srcdir)/$modpath_current]) APR_ADDTO(INCLUDES, [-I\$(top_srcdir)/$modpath_current])

View File

@@ -36,6 +36,13 @@ APR_DECLARE_OPTIONAL_FN(char *, ssl_var_lookup,
#define MAX(x,y) ((x) >= (y) ? (x) : (y)) #define MAX(x,y) ((x) >= (y) ? (x) : (y))
#endif #endif
/*
* We do health-checks only if that (sub)module is loaded in. This
* allows for us to continue as is w/o requiring mod_watchdog for
* those implementations which aren't using health checks
*/
static APR_OPTIONAL_FN_TYPE(set_worker_hc_param) *set_worker_hc_param_f = NULL;
static const char * const proxy_id = "proxy"; static const char * const proxy_id = "proxy";
apr_global_mutex_t *proxy_mutex = NULL; apr_global_mutex_t *proxy_mutex = NULL;
@@ -274,7 +281,11 @@ static const char *set_worker_param(apr_pool_t *p,
PROXY_STRNCPY(worker->s->flusher, val); PROXY_STRNCPY(worker->s->flusher, val);
} }
else { else {
return "unknown Worker parameter"; if (set_worker_hc_param_f) {
return set_worker_hc_param_f(p, worker, key, val, NULL);
} else {
return "unknown Worker parameter";
}
} }
return NULL; return NULL;
} }
@@ -2667,6 +2678,7 @@ static int proxy_post_config(apr_pool_t *pconf, apr_pool_t *plog,
proxy_ssl_disable = APR_RETRIEVE_OPTIONAL_FN(ssl_engine_disable); proxy_ssl_disable = APR_RETRIEVE_OPTIONAL_FN(ssl_engine_disable);
proxy_is_https = APR_RETRIEVE_OPTIONAL_FN(ssl_is_https); proxy_is_https = APR_RETRIEVE_OPTIONAL_FN(ssl_is_https);
proxy_ssl_val = APR_RETRIEVE_OPTIONAL_FN(ssl_var_lookup); proxy_ssl_val = APR_RETRIEVE_OPTIONAL_FN(ssl_var_lookup);
set_worker_hc_param_f = APR_RETRIEVE_OPTIONAL_FN(set_worker_hc_param);
ap_proxy_strmatch_path = apr_strmatch_precompile(pconf, "path=", 0); ap_proxy_strmatch_path = apr_strmatch_precompile(pconf, "path=", 0);
ap_proxy_strmatch_domain = apr_strmatch_precompile(pconf, "domain=", 0); ap_proxy_strmatch_domain = apr_strmatch_precompile(pconf, "domain=", 0);
@@ -2889,7 +2901,8 @@ static void register_hooks(apr_pool_t *p)
* make sure that we are called after the mpm * make sure that we are called after the mpm
* initializes. * initializes.
*/ */
static const char *const aszPred[] = { "mpm_winnt.c", "mod_proxy_balancer.c", NULL}; static const char *const aszPred[] = { "mpm_winnt.c", "mod_proxy_balancer.c",
"mod_proxy_hcheck.c", NULL};
/* handler */ /* handler */
ap_hook_handler(proxy_handler, NULL, NULL, APR_HOOK_FIRST); ap_hook_handler(proxy_handler, NULL, NULL, APR_HOOK_FIRST);

View File

@@ -359,6 +359,7 @@ typedef struct {
char redirect[PROXY_WORKER_MAX_ROUTE_SIZE]; /* temporary balancing redirection route */ char redirect[PROXY_WORKER_MAX_ROUTE_SIZE]; /* temporary balancing redirection route */
char flusher[PROXY_WORKER_MAX_SCHEME_SIZE]; /* flush provider used by mod_proxy_fdpass */ char flusher[PROXY_WORKER_MAX_SCHEME_SIZE]; /* flush provider used by mod_proxy_fdpass */
char uds_path[PROXY_WORKER_MAX_NAME_SIZE]; /* path to worker's unix domain socket if applicable */ char uds_path[PROXY_WORKER_MAX_NAME_SIZE]; /* path to worker's unix domain socket if applicable */
char hurl[PROXY_WORKER_MAX_ROUTE_SIZE]; /* health check url */
int lbset; /* load balancer cluster set */ int lbset; /* load balancer cluster set */
int retries; /* number of retries on this worker */ int retries; /* number of retries on this worker */
int lbstatus; /* Current lbstatus */ int lbstatus; /* Current lbstatus */
@@ -368,6 +369,9 @@ typedef struct {
int hmax; /* Hard maximum on the total number of connections */ int hmax; /* Hard maximum on the total number of connections */
int flush_wait; /* poll wait time in microseconds if flush_auto */ int flush_wait; /* poll wait time in microseconds if flush_auto */
int index; /* shm array index */ int index; /* shm array index */
int method; /* method to use for health check */
int passes; /* number of successes for check to pass */
int fails; /* number of failures for check to fail */
proxy_hashes hash; /* hash of worker name */ proxy_hashes hash; /* hash of worker name */
unsigned int status; /* worker status bitfield */ unsigned int status; /* worker status bitfield */
enum { enum {
@@ -384,6 +388,7 @@ typedef struct {
apr_interval_time_t acquire; /* acquire timeout when the maximum number of connections is exceeded */ apr_interval_time_t acquire; /* acquire timeout when the maximum number of connections is exceeded */
apr_interval_time_t ping_timeout; apr_interval_time_t ping_timeout;
apr_interval_time_t conn_timeout; apr_interval_time_t conn_timeout;
apr_interval_time_t interval;
apr_size_t recv_buffer_size; apr_size_t recv_buffer_size;
apr_size_t io_buffer_size; apr_size_t io_buffer_size;
apr_size_t elected; /* Number of times the worker was elected */ apr_size_t elected; /* Number of times the worker was elected */
@@ -519,6 +524,10 @@ struct proxy_balancer_method {
#define PROXY_DECLARE_DATA __declspec(dllimport) #define PROXY_DECLARE_DATA __declspec(dllimport)
#endif #endif
APR_DECLARE_OPTIONAL_FN(const char *, set_worker_hc_param,
(apr_pool_t *, proxy_worker *,
const char *, const char *, void *));
APR_DECLARE_EXTERNAL_HOOK(proxy, PROXY, int, scheme_handler, (request_rec *r, APR_DECLARE_EXTERNAL_HOOK(proxy, PROXY, int, scheme_handler, (request_rec *r,
proxy_worker *worker, proxy_server_conf *conf, char *url, proxy_worker *worker, proxy_server_conf *conf, char *url,
const char *proxyhost, apr_port_t proxyport)) const char *proxyhost, apr_port_t proxyport))

View File

@@ -0,0 +1,279 @@
/* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF licenses this file to You under the Apache License, Version 2.0
* (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#include "mod_proxy.h"
#include "mod_watchdog.h"
module AP_MODULE_DECLARE_DATA proxy_hcheck_module;
#define HCHECK_WATHCHDOG_NAME ("_proxy_hcheck_")
/* default to health check every 30 seconds */
#define HCHECK_WATHCHDOG_SEC (30)
/* The watchdog runs every 5 seconds, which is also the minimal check */
#define HCHECK_WATHCHDOG_INTERVAL (5)
static char *methods[] = {
"NULL", "OPTIONS", "HEAD", "GET", "POST", "CPING"
};
typedef struct hcheck_template_t {
char *name;
int method;
int passes;
int fails;
apr_interval_time_t interval;
char *hurl;
} hcheck_template_t;
static apr_pool_t *ptemplate = NULL;
static apr_array_header_t *templates = NULL;
static ap_watchdog_t *watchdog;
/*
* This is not as clean as it should be, because we are using
* the same to both update the actual worker as well as verifying
* and populating the health check 'template' as well.
*/
static const char *set_worker_hc_param(apr_pool_t *p,
proxy_worker *worker,
const char *key,
const char *val,
void *tmp)
{
int ival;
hcheck_template_t *ctx;
if (!worker && !tmp) {
return "Bad call to set_worker_hc_param()";
}
ctx = (hcheck_template_t *)tmp;
if (!strcasecmp(key, "hcheck")) {
hcheck_template_t *template;
template = (hcheck_template_t *)templates->elts;
for (ival = 0; ival < templates->nelts; ival++, template++) {
if (!ap_casecmpstr(template->name, val)) {
worker->s->method = template->method;
worker->s->interval = template->interval;
worker->s->passes = template->passes;
worker->s->fails = template->fails;
PROXY_STRNCPY(worker->s->hurl, template->hurl);
return NULL;
}
}
return apr_psprintf(p, "Unknown HCheckTemplate name: %s", val);
}
else if (!strcasecmp(key, "method")) {
for (ival = 1; ival < sizeof(methods); ival++) {
if (!ap_casecmpstr(val, methods[ival])) {
if (worker) {
worker->s->method = ival;
} else {
ctx->method = ival;
}
return NULL;
}
}
return "Unknown method";
}
else if (!strcasecmp(key, "interval")) {
ival = atoi(val);
if (ival < 5)
return "Interval must be a positive value greater than 5 seconds";
if (worker) {
worker->s->interval = apr_time_from_sec(ival);
} else {
ctx->interval = apr_time_from_sec(ival);
}
}
else if (!strcasecmp(key, "passes")) {
ival = atoi(val);
if (ival < 0)
return "Passes must be a positive value";
if (worker) {
worker->s->passes = ival;
} else {
ctx->passes = ival;
}
}
else if (!strcasecmp(key, "fails")) {
ival = atoi(val);
if (ival < 0)
return "Fails must be a positive value";
if (worker) {
worker->s->fails = ival;
} else {
ctx->fails = ival;
}
}
else if (!strcasecmp(key, "hurl")) {
if (strlen(val) >= sizeof(worker->s->hurl))
return apr_psprintf(p, "Health check hurl length must be < %d characters",
(int)sizeof(worker->s->hurl));
if (worker) {
PROXY_STRNCPY(worker->s->hurl, val);
} else {
ctx->hurl = apr_pstrdup(p, val);
}
}
else {
return "unknown Worker hcheck parameter";
}
return NULL;
}
static const char *set_hcheck(cmd_parms *cmd, void *dummy, const char *arg)
{
char *name = NULL;
char *word, *val;
hcheck_template_t template;
hcheck_template_t *tpush;
const char *err = ap_check_cmd_context(cmd, NOT_IN_HTACCESS);
if (err)
return err;
template.name = ap_getword_conf(cmd->temp_pool, &arg);
template.method = template.passes = template.fails = 1;
template.interval = apr_time_from_sec(HCHECK_WATHCHDOG_SEC);
template.hurl = NULL;
while (*arg) {
word = ap_getword_conf(cmd->pool, &arg);
val = strchr(word, '=');
if (!val) {
return "Invalid HCheckTemplate parameter. Parameter must be "
"in the form 'key=value'";
}
else
*val++ = '\0';
err = set_worker_hc_param(cmd->pool, NULL, word, val, &template);
if (err)
return apr_pstrcat(cmd->temp_pool, "HCheckTemplate: ", err, " ", word, "=", val, "; ", name, NULL);
/* No error means we have a valid template */
tpush = (hcheck_template_t *)apr_array_push(templates);
memcpy(tpush, &template, sizeof(hcheck_template_t));
}
return NULL;
}
static apr_status_t hc_watchdog_callback(int state, void *data,
apr_pool_t *pool)
{
apr_status_t rv = APR_SUCCESS;
apr_time_t cur, now;
switch (state) {
case AP_WATCHDOG_STATE_STARTING:
ap_log_error(APLOG_MARK, APLOG_DEBUG, 0, ap_server_conf, APLOGNO()
"%s watchdog started.",
HCHECK_WATHCHDOG_NAME);
break;
case AP_WATCHDOG_STATE_RUNNING:
cur = now = apr_time_sec(apr_time_now());
/*
while ((now - cur) < apr_time_sec(ctx->interval)) {
break;
}
*/
break;
case AP_WATCHDOG_STATE_STOPPING:
ap_log_error(APLOG_MARK, APLOG_DEBUG, 0, ap_server_conf, APLOGNO()
"stopping %s watchdog.",
HCHECK_WATHCHDOG_NAME);
break;
}
return rv;
}
static int hc_pre_config(apr_pool_t *p, apr_pool_t *plog,
apr_pool_t *ptemp)
{
if (!ptemplate) {
apr_pool_create(&ptemplate, p);
}
if (!templates) {
templates = apr_array_make(ptemplate, 10, sizeof(hcheck_template_t));
}
return OK;
}
static int hc_post_config(apr_pool_t *p, apr_pool_t *plog,
apr_pool_t *ptemp, server_rec *s)
{
apr_status_t rv;
APR_OPTIONAL_FN_TYPE(ap_watchdog_get_instance) *hc_watchdog_get_instance;
APR_OPTIONAL_FN_TYPE(ap_watchdog_register_callback) *hc_watchdog_register_callback;
hc_watchdog_get_instance = APR_RETRIEVE_OPTIONAL_FN(ap_watchdog_get_instance);
hc_watchdog_register_callback = APR_RETRIEVE_OPTIONAL_FN(ap_watchdog_register_callback);
if (!hc_watchdog_get_instance || !hc_watchdog_register_callback) {
ap_log_error(APLOG_MARK, APLOG_CRIT, 0, s, APLOGNO()
"mod_watchdog is required");
return !OK;
}
rv = hc_watchdog_get_instance(&watchdog,
HCHECK_WATHCHDOG_NAME,
0, 1, p);
if (rv) {
ap_log_error(APLOG_MARK, APLOG_CRIT, rv, s, APLOGNO()
"Failed to create watchdog instance (%s)",
HCHECK_WATHCHDOG_NAME);
return !OK;
}
rv = hc_watchdog_register_callback(watchdog,
apr_time_from_sec(HCHECK_WATHCHDOG_INTERVAL),
NULL,
hc_watchdog_callback);
if (rv) {
ap_log_error(APLOG_MARK, APLOG_CRIT, rv, s, APLOGNO()
"Failed to register watchdog callback (%s)",
HCHECK_WATHCHDOG_NAME);
return !OK;
}
ap_log_error(APLOG_MARK, APLOG_DEBUG, 0, s, APLOGNO()
"watchdog callback registered (%s)", HCHECK_WATHCHDOG_NAME);
return OK;
}
static const command_rec command_table[] = {
AP_INIT_RAW_ARGS("HCheckTemplate", set_hcheck, NULL, OR_FILEINFO,
"Health check template"),
{ NULL }
};
static void hc_register_hooks(apr_pool_t *p)
{
static const char *const runAfter[] = { "mod_watchdog.c", NULL};
APR_REGISTER_OPTIONAL_FN(set_worker_hc_param);
ap_hook_pre_config(hc_pre_config, NULL, NULL, APR_HOOK_LAST);
ap_hook_post_config(hc_post_config, NULL, runAfter, APR_HOOK_LAST);
}
/* the main config structure */
AP_DECLARE_MODULE(proxy_hcheck) =
{
STANDARD20_MODULE_STUFF,
NULL, /* create per-dir config structures */
NULL, /* merge per-dir config structures */
NULL, /* create per-server config structures */
NULL, /* merge per-server config structures */
command_table, /* table of config file commands */
hc_register_hooks /* register hooks */
};