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:
@@ -10,6 +10,10 @@ else
|
||||
proxy_mods_enable=most
|
||||
fi
|
||||
|
||||
if test "$proxy_mods_enable" = "no"; then
|
||||
enable_proxy_hcheck=no
|
||||
fi
|
||||
|
||||
proxy_objs="mod_proxy.lo proxy_util.lo"
|
||||
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_hcheck, reverse-proxy health-check module. Requires --enable-proxy and --enable-watchdog., , , $enable_proxy_hcheck,, watchdog)
|
||||
|
||||
APR_ADDTO(INCLUDES, [-I\$(top_srcdir)/$modpath_current])
|
||||
|
||||
|
@@ -36,6 +36,13 @@ APR_DECLARE_OPTIONAL_FN(char *, ssl_var_lookup,
|
||||
#define MAX(x,y) ((x) >= (y) ? (x) : (y))
|
||||
#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";
|
||||
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);
|
||||
}
|
||||
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;
|
||||
}
|
||||
@@ -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_is_https = APR_RETRIEVE_OPTIONAL_FN(ssl_is_https);
|
||||
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_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
|
||||
* 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 */
|
||||
ap_hook_handler(proxy_handler, NULL, NULL, APR_HOOK_FIRST);
|
||||
|
@@ -359,6 +359,7 @@ typedef struct {
|
||||
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 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 retries; /* number of retries on this worker */
|
||||
int lbstatus; /* Current lbstatus */
|
||||
@@ -368,6 +369,9 @@ typedef struct {
|
||||
int hmax; /* Hard maximum on the total number of connections */
|
||||
int flush_wait; /* poll wait time in microseconds if flush_auto */
|
||||
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 */
|
||||
unsigned int status; /* worker status bitfield */
|
||||
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 ping_timeout;
|
||||
apr_interval_time_t conn_timeout;
|
||||
apr_interval_time_t interval;
|
||||
apr_size_t recv_buffer_size;
|
||||
apr_size_t io_buffer_size;
|
||||
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)
|
||||
#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,
|
||||
proxy_worker *worker, proxy_server_conf *conf, char *url,
|
||||
const char *proxyhost, apr_port_t proxyport))
|
||||
|
279
modules/proxy/mod_proxy_hcheck.c
Normal file
279
modules/proxy/mod_proxy_hcheck.c
Normal 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 */
|
||||
};
|
Reference in New Issue
Block a user