From db214364d05e7db624c9b6ca18d1bcc2617e3520 Mon Sep 17 00:00:00 2001 From: Willy Tarreau Date: Wed, 27 Jan 2010 11:53:01 +0100 Subject: [PATCH] [MEDIUM] checks: add the server's status in the checks Now a server can check the contents of the header X-Haproxy-Server-State to know how haproxy sees it. The same values as those reported in the stats are provided : - up/down status + check counts - throttle - weight vs backend weight - active sessions vs backend sessions - queue length - haproxy node name --- doc/configuration.txt | 43 +++++++++++++++++++++++++++++++ include/types/proxy.h | 3 +++ src/cfgparse.c | 13 +++++++++- src/checks.c | 60 +++++++++++++++++++++++++++++++++++++++++++ 4 files changed, 118 insertions(+), 1 deletion(-) diff --git a/doc/configuration.txt b/doc/configuration.txt index c96051ba2..7b941c42c 100644 --- a/doc/configuration.txt +++ b/doc/configuration.txt @@ -1711,6 +1711,49 @@ http-check disable-on-404 See also : "option httpchk" +http-check send-state + Enable emission of a state header with HTTP health checks + May be used in sections : defaults | frontend | listen | backend + yes | no | yes | yes + Arguments : none + + When this option is set, haproxy will systematically send a special header + "X-Haproxy-Server-State" with a list of parameters indicating to each server + how they are seen by haproxy. This can be used for instance when a server is + manipulated without access to haproxy and the operator needs to know whether + haproxy still sees it up or not, or if the server is the last one in a farm. + + The header is composed of fields delimited by semi-colons, the first of which + is a word ("UP", "DOWN", "NOLB"), possibly followed by a number of valid + checks on the total number before transition, just as appears in the stats + interface. Next headers are in the form "=", indicating in + no specific order some values available in the stats interface : + - a variable "name", containing the name of the backend followed by a slash + ("/") then the name of the server. This can be used when a server is + checked in multiple backends. + + - a variable "node" containing the name of the haproxy node, as set in the + global "node" variable, otherwise the system's hostname if unspecified. + + - a variable "weight" indicating the weight of the server, a slash ("/") + and the total weight of the farm (just counting usable servers). This + helps to know if other servers are available to handle the load when this + one fails. + + - a variable "scur" indicating the current number of concurrent connections + on the server, followed by a slash ("/") then the total number of + connections on all servers of the same backend. + + - a variable "qcur" indicating the current number of requests in the + server's queue. + + Example of a header received by the application server : + >>> X-Haproxy-Server-State: UP 2/3; name=bck/srv2; node=lb1; weight=1/2; \ + scur=13/22; qcur=0 + + See also : "option httpchk", "http-check disable-on-404" + + id Set a persistent value for proxy ID. Must be unique and larger than 1000, as smaller values are reserved for auto-assigned ids. diff --git a/include/types/proxy.h b/include/types/proxy.h index 67681d2c4..1eee3bea8 100644 --- a/include/types/proxy.h +++ b/include/types/proxy.h @@ -121,6 +121,9 @@ #define PR_O2_INDEPSTR 0x00001000 /* independant streams, don't update rex on write */ /* 0x2000 used in 1.4 */ #define PR_O2_AS_REQL 0x00004000 /* appsession: learn the session id from the request */ +/* 0x8000 to 0x40000 used in 1.4 */ +#define PR_O2_CHK_SNDST 0x00080000 /* send the state of each server along with HTTP health checks */ +/* end of proxy->options2 */ /* This structure is used to apply fast weighted round robin on a server group */ struct fwrr_group { diff --git a/src/cfgparse.c b/src/cfgparse.c index 4765a527d..1c1452dd8 100644 --- a/src/cfgparse.c +++ b/src/cfgparse.c @@ -2198,6 +2198,10 @@ int cfg_parse_listen(const char *file, int linenum, char **args, int inv) /* enable a graceful server shutdown on an HTTP 404 response */ curproxy->options |= PR_O_DISABLE404; } + else if (strcmp(args[1], "send-state") == 0) { + /* enable emission of the apparent state of a server in HTTP checks */ + curproxy->options2 |= PR_O2_CHK_SNDST; + } else { Alert("parsing [%s:%d] : '%s' only supports 'disable-on-404'.\n", file, linenum, args[0]); err_code |= ERR_ALERT | ERR_FATAL; @@ -4000,6 +4004,13 @@ int check_config_validity() err_code |= ERR_WARN; } + if ((curproxy->options2 & PR_O2_CHK_SNDST) && !(curproxy->options & PR_O_HTTP_CHK)) { + curproxy->options &= ~PR_O2_CHK_SNDST; + Warning("config : '%s' will be ignored for %s '%s' (requires 'option httpchk').\n", + "send-state", proxy_type_str(curproxy), curproxy->id); + err_code |= ERR_WARN; + } + /* if a default backend was specified, let's find it */ if (curproxy->defbe.name) { struct proxy *target; @@ -4268,7 +4279,7 @@ int check_config_validity() if (curproxy != px && (curproxy->options & PR_O_DISABLE404) != (px->options & PR_O_DISABLE404)) { Alert("config : %s '%s', server '%s': unable to use %s/%s for" - "tracing: disable-on-404 option inconsistency.\n", + "tracking: disable-on-404 option inconsistency.\n", proxy_type_str(curproxy), curproxy->id, newsrv->id, px->id, srv->id); cfgerr++; diff --git a/src/checks.c b/src/checks.c index 5445b8831..0039161f7 100644 --- a/src/checks.c +++ b/src/checks.c @@ -324,6 +324,62 @@ static void set_server_enabled(struct server *s) { set_server_enabled(srv); } +static int httpchk_build_status_header(struct server *s, char *buffer) +{ + int sv_state; + int ratio; + int hlen = 0; + const char *srv_hlt_st[7] = { "DOWN", "DOWN %d/%d", + "UP %d/%d", "UP", + "NOLB %d/%d", "NOLB", + "no check" }; + + memcpy(buffer + hlen, "X-Haproxy-Server-State: ", 24); + hlen += 24; + + if (!(s->state & SRV_CHECKED)) + sv_state = 6; /* should obviously never happen */ + else if (s->state & SRV_RUNNING) { + if (s->health == s->rise + s->fall - 1) + sv_state = 3; /* UP */ + else + sv_state = 2; /* going down */ + + if (s->state & SRV_GOINGDOWN) + sv_state += 2; + } else { + if (s->health) + sv_state = 1; /* going up */ + else + sv_state = 0; /* DOWN */ + } + + hlen += sprintf(buffer + hlen, + srv_hlt_st[sv_state], + (s->state & SRV_RUNNING) ? (s->health - s->rise + 1) : (s->health), + (s->state & SRV_RUNNING) ? (s->fall) : (s->rise)); + + hlen += sprintf(buffer + hlen, "; name=%s/%s; node=%s; weight=%d/%d; scur=%d/%d; qcur=%d", + s->proxy->id, s->id, + global.node, + (s->eweight * s->proxy->lbprm.wmult + s->proxy->lbprm.wdiv - 1) / s->proxy->lbprm.wdiv, + (s->proxy->lbprm.tot_weight * s->proxy->lbprm.wmult + s->proxy->lbprm.wdiv - 1) / s->proxy->lbprm.wdiv, + s->cur_sess, s->proxy->beconn - s->proxy->nbpend, + s->nbpend); + + if ((s->state & SRV_WARMINGUP) && + now.tv_sec < s->last_change + s->slowstart && + now.tv_sec >= s->last_change) { + ratio = MAX(1, 100 * (now.tv_sec - s->last_change) / s->slowstart); + hlen += sprintf(buffer + hlen, "; throttle=%d%%", ratio); + } + + buffer[hlen++] = '\r'; + buffer[hlen++] = '\n'; + + return hlen; +} + /* * This function is used only for server health-checks. It handles * the connection acknowledgement. If the proxy requires HTTP health-checks, @@ -363,6 +419,10 @@ static int event_srv_chk_w(int fd) } else if (s->proxy->options & PR_O_HTTP_CHK) { memcpy(trash, check_req, check_len); + + if (s->proxy->options2 & PR_O2_CHK_SNDST) + check_len += httpchk_build_status_header(s, trash + check_len); + trash[check_len++] = '\r'; trash[check_len++] = '\n'; trash[check_len] = '\0';