From 75107d3319f77f4fcf7f5cfe8174046aca1766cf Mon Sep 17 00:00:00 2001
From: Krzysztof Piotr Oledzki <ole#ans.pl>
Date: Tue, 5 Feb 2008 21:29:31 +0100
Subject: [RFC] implement "track backend/server"
This rfc-quality patch implements ability to set a current state of one server by tracking another one. It:
TODO: documentation, more tests
--- TODO | 3 - include/proto/proxy.h | 1 + include/types/server.h | 1 + src/cfgparse.c | 61 +++++++++++- src/checks.c | 275 +++++++++++++++++++++++++++++++---------------- src/dumpstats.c | 44 +++++--- src/proxy.c | 27 +++++- 7 files changed, 299 insertions(+), 113 deletions(-) diff --git a/TODO b/TODO index ae120a1..74b3cce 100644 --- a/TODO +++ b/TODOReceived on 2008/02/05 21:37
@@ -174,9 +174,6 @@ TODO for 1.3
filters and backend, on which every entity could rely. - implement 'on uri <uri> <proxy>', 'on host <host> <proxy>' - remove the first now useless hop in hdr_idx - - implement "track XXX.YYY" for each server as an alternative to - health checks. This will automatically set the server state to - the same as server YYY of proxy XXX. - balance on URI hash (specify length or depth) - balance on any header hash (eg: host) - balance with redirections to real servers diff --git a/include/proto/proxy.h b/include/proto/proxy.h index f00f646..dbe0f09 100644 --- a/include/proto/proxy.h +++ b/include/proto/proxy.h
@@ -36,6 +36,7 @@ void listen_proxies(void);
const char *proxy_cap_str(int cap); const char *proxy_mode_str(int mode); struct proxy *findproxy(const char *name, int mode, int cap); +struct server *findserver(const struct proxy *px, const char *name); int proxy_parse_timeout(const char **args, struct proxy *proxy, struct proxy *defpx, char *err, int errlen); diff --git a/include/types/server.h b/include/types/server.h index cfc4d7d..da411c0 100644 --- a/include/types/server.h +++ b/include/types/server.h
@@ -89,6 +89,7 @@ struct server {
struct sockaddr_in tproxy_addr; /* non-local address we want to bind to for connect() */ #endif + struct server *tracknext, *tracked; /* next server in a tracking list, tracked server */ struct sockaddr_in check_addr; /* the address to check, if different from <addr> */ short check_port; /* the port to use for the health checks */ int health; /* 0->rise-1 = bad; rise->rise+fall-1 = good */ diff --git a/src/cfgparse.c b/src/cfgparse.c index 1815ed4..f99f84c 100644 --- a/src/cfgparse.c +++ b/src/cfgparse.c
@@ -1467,7 +1467,7 @@ int cfg_parse_listen(const char *file, int linenum, char **args, int inv)
char *rport; char *raddr; short realport; - int do_check; + int do_check, do_track; if (curproxy == &defproxy) { Alert("parsing [%s:%d] : '%s' not allowed in 'defaults' section.\n", file, linenum, args[0]);
@@ -1502,6 +1502,7 @@ int cfg_parse_listen(const char *file, int linenum, char **args, int inv)
LIST_INIT(&newsrv->pendconns); do_check = 0; + do_track = 0; newsrv->state = SRV_RUNNING; /* early server setup */ newsrv->last_change = now.tv_sec; newsrv->id = strdup(args[1]);
@@ -1632,6 +1633,58 @@ int cfg_parse_listen(const char *file, int linenum, char **args, int inv)
newsrv->slowstart = (val + 999) / 1000; cur_arg += 2; } + else if (!strcmp(args[cur_arg], "track")) { + struct proxy *px; + struct server *srv; + char *pname, *sname; + + pname = args[cur_arg + 1]; + sname = strrchr(pname, '/'); + + if (sname) + *sname++ = '\0'; + else { + sname = pname; + pname = NULL; + } + + if (!*sname) { + Alert("parsing [%s:%d]: 'track' expects [<proxy>/]<server> as argument.\n", + file, linenum); + return -1; + } + + if (pname) { + px = findproxy(pname, curproxy->mode, PR_CAP_BE); + if (!px) { + Alert("parsing [%s:%d]: unable to find required proxy '%s' for 'track'.\n", + file, linenum, pname); + return -1; + } + + } else + px = curproxy; + + srv = findserver(px, sname); + if (!srv) { + Alert("parsing [%s:%d]: unable to find required server '%s' for 'track'.\n", + file, linenum, sname); + return -1; + } + + if (!(srv->state & SRV_CHECKED)) { + Alert("parsing [%s:%d]: unable to use %s/%s for tracing as it does not have checks enabled.\n", + file, linenum, px->id, srv->id); + return -1; + } + + newsrv->tracked = srv; + newsrv->tracknext = srv->tracknext; + srv->tracknext = newsrv; + + do_track = 1; + cur_arg += 2; + } else if (!strcmp(args[cur_arg], "check")) { global.maxsock++; do_check = 1;
@@ -1697,6 +1750,12 @@ int cfg_parse_listen(const char *file, int linenum, char **args, int inv)
} } + if (do_check && do_track) { + Alert("parsing [%s:%d]: unable to enable checks and tracking at the same time!\n", + file, linenum); + return -1; + } + if (do_check) { if (!newsrv->check_port && newsrv->check_addr.sin_port) newsrv->check_port = newsrv->check_addr.sin_port; diff --git a/src/checks.c b/src/checks.c index 0557528..5485a13 100644 --- a/src/checks.c +++ b/src/checks.c
@@ -2,6 +2,7 @@
* Health-checks functions. * * Copyright 2000-2008 Willy Tarreau <w#1wt.eu> + * Copyright 2007-2008 Krzysztof Piotr Oledzki <ole#ans.pl> * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License
@@ -34,6 +35,7 @@
#include <types/session.h> #include <proto/backend.h> +#include <proto/buffers.h> #include <proto/fd.h> #include <proto/log.h> #include <proto/queue.h>
@@ -118,9 +120,11 @@ static int check_for_pending(struct server *s)
*/ static void set_server_down(struct server *s) { + struct server *srv; + struct chunk msg; int xferred; - if (s->health == s->rise) { + if (s->health == s->rise || s->tracked) { int srv_was_paused = s->state & SRV_GOINGDOWN; s->last_change = now.tv_sec;
@@ -132,10 +136,21 @@ static void set_server_down(struct server *s)
* to another server or to the proxy itself. */ xferred = redistribute_pending(s); - sprintf(trash, "%sServer %s/%s is DOWN. %d active and %d backup servers left.%s" + + msg.len = 0; + msg.str = trash; + + chunk_printf(&msg, sizeof(trash), + "%sServer %s/%s is DOWN", s->state & SRV_BACKUP ? "Backup " : "", + s->proxy->id, s->id); + + if (s->tracked) + chunk_printf(&msg, sizeof(trash), " via %s/%s", + s->tracked->proxy->id, s->tracked->id); + + chunk_printf(&msg, sizeof(trash), ". %d active and %d backup servers left.%s" " %d sessions active, %d requeued, %d remaining in queue.\n", - s->state & SRV_BACKUP ? "Backup " : "", - s->proxy->id, s->id, s->proxy->srv_act, s->proxy->srv_bck, + s->proxy->srv_act, s->proxy->srv_bck, (s->proxy->srv_bck && !s->proxy->srv_act) ? " Running on backup." : "", s->cur_sess, xferred, s->nbpend);
@@ -151,10 +166,167 @@ static void set_server_down(struct server *s)
set_backend_down(s->proxy); s->down_trans++; + + if (s->state && SRV_CHECKED) + for(srv = s->tracknext; srv; srv = srv->tracknext) + set_server_down(srv); } + s->health = 0; /* failure */ } +static void set_server_up(struct server *s) { + + struct server *srv; + struct chunk msg; + int xferred; + + if (s->health == s->rise || s->tracked) { + if (s->proxy->srv_bck == 0 && s->proxy->srv_act == 0) { + if (s->proxy->last_change < now.tv_sec) // ignore negative times + s->proxy->down_time += now.tv_sec - s->proxy->last_change; + s->proxy->last_change = now.tv_sec; + } + + if (s->last_change < now.tv_sec) // ignore negative times + s->down_time += now.tv_sec - s->last_change; + + s->last_change = now.tv_sec; + s->state |= SRV_RUNNING; + + if (s->slowstart > 0) { + s->state |= SRV_WARMINGUP; + if (s->proxy->lbprm.algo & BE_LB_PROP_DYN) { + /* For dynamic algorithms, start at the first step of the weight, + * without multiplying by BE_WEIGHT_SCALE. + */ + s->eweight = s->uweight; + if (s->proxy->lbprm.update_server_eweight) + s->proxy->lbprm.update_server_eweight(s); + } + } + s->proxy->lbprm.set_server_status_up(s); + + /* check if we can handle some connections queued at the proxy. We + * will take as many as we can handle. + */ + xferred = check_for_pending(s); + + msg.len = 0; + msg.str = trash; + + chunk_printf(&msg, sizeof(trash), + "%sServer %s/%s is UP", s->state & SRV_BACKUP ? "Backup " : "", + s->proxy->id, s->id); + + if (s->tracked) + chunk_printf(&msg, sizeof(trash), " via %s/%s", + s->tracked->proxy->id, s->tracked->id); + + chunk_printf(&msg, sizeof(trash), ". %d active and %d backup servers online.%s" + " %d sessions requeued, %d total in queue.\n", + s->proxy->srv_act, s->proxy->srv_bck, + (s->proxy->srv_bck && !s->proxy->srv_act) ? " Running on backup." : "", + s->cur_sess, xferred, s->nbpend); + + Warning("%s", trash); + send_log(s->proxy, LOG_NOTICE, "%s", trash); + + if (s->state && SRV_CHECKED) + for(srv = s->tracknext; srv; srv = srv->tracknext) + set_server_up(srv); + } + + if (s->health >= s->rise) + s->health = s->rise + s->fall - 1; /* OK now */ + +} + +static void set_server_disabled(struct server *s) { + + struct server *srv; + struct chunk msg; + int xferred; + + s->state |= SRV_GOINGDOWN; + s->proxy->lbprm.set_server_status_down(s); + + /* we might have sessions queued on this server and waiting for + * a connection. Those which are redispatchable will be queued + * to another server or to the proxy itself. + */ + xferred = redistribute_pending(s); + + msg.len = 0; + msg.str = trash; + + chunk_printf(&msg, sizeof(trash), + "Load-balancing on %sServer %s/%s is disabled", + s->state & SRV_BACKUP ? "Backup " : "", + s->proxy->id, s->id); + + if (s->tracked) + chunk_printf(&msg, sizeof(trash), " via %s/%s", + s->tracked->proxy->id, s->tracked->id); + + + chunk_printf(&msg, sizeof(trash),". %d active and %d backup servers online.%s" + " %d sessions requeued, %d total in queue.\n", + s->proxy->srv_act, s->proxy->srv_bck, + (s->proxy->srv_bck && !s->proxy->srv_act) ? " Running on backup." : "", + xferred, s->nbpend); + + Warning("%s", trash); + + send_log(s->proxy, LOG_NOTICE, "%s", trash); + + if (!s->proxy->srv_bck && !s->proxy->srv_act) + set_backend_down(s->proxy); + + if (s->state && SRV_CHECKED) + for(srv = s->tracknext; srv; srv = srv->tracknext) + set_server_disabled(srv); +} + +static void set_server_enabled(struct server *s) { + + struct server *srv; + struct chunk msg; + int xferred; + + s->state &= ~SRV_GOINGDOWN; + s->proxy->lbprm.set_server_status_up(s); + + /* check if we can handle some connections queued at the proxy. We + * will take as many as we can handle. + */ + xferred = check_for_pending(s); + + msg.len = 0; + msg.str = trash; + + chunk_printf(&msg, sizeof(trash), + "Load-balancing on %sServer %s/%s is enabled again", + s->state & SRV_BACKUP ? "Backup " : "", + s->proxy->id, s->id); + + if (s->tracked) + chunk_printf(&msg, sizeof(trash), " via %s/%s", + s->tracked->proxy->id, s->tracked->id); + + chunk_printf(&msg, sizeof(trash), ". %d active and %d backup servers online.%s" + " %d sessions requeued, %d total in queue.\n", + s->proxy->srv_act, s->proxy->srv_bck, + (s->proxy->srv_bck && !s->proxy->srv_act) ? " Running on backup." : "", + xferred, s->nbpend); + + Warning("%s", trash); + send_log(s->proxy, LOG_NOTICE, "%s", trash); + + if (s->state && SRV_CHECKED) + for(srv = s->tracknext; srv; srv = srv->tracknext) + set_server_enabled(srv); +} /* * This function is used only for server health-checks. It handles
@@ -568,103 +740,18 @@ void process_chk(struct task *t, struct timeval *next)
/* we may have to add/remove this server from the LB group */ if ((s->state & SRV_RUNNING) && (s->proxy->options & PR_O_DISABLE404)) { if ((s->state & SRV_GOINGDOWN) && - ((s->result & (SRV_CHK_RUNNING|SRV_CHK_DISABLE)) == SRV_CHK_RUNNING)) { - /* server enabled again */ - s->state &= ~SRV_GOINGDOWN; - s->proxy->lbprm.set_server_status_up(s); - - /* check if we can handle some connections queued at the proxy. We - * will take as many as we can handle. - */ - xferred = check_for_pending(s); - - sprintf(trash, - "Load-balancing on %sServer %s/%s is enabled again. %d active and %d backup servers online.%s" - " %d sessions requeued, %d total in queue.\n", - s->state & SRV_BACKUP ? "Backup " : "", - s->proxy->id, s->id, s->proxy->srv_act, s->proxy->srv_bck, - (s->proxy->srv_bck && !s->proxy->srv_act) ? " Running on backup." : "", - xferred, s->nbpend); - - Warning("%s", trash); - send_log(s->proxy, LOG_NOTICE, "%s", trash); - } + ((s->result & (SRV_CHK_RUNNING|SRV_CHK_DISABLE)) == SRV_CHK_RUNNING)) + set_server_enabled(s); else if (!(s->state & SRV_GOINGDOWN) && ((s->result & (SRV_CHK_RUNNING | SRV_CHK_DISABLE)) == - (SRV_CHK_RUNNING | SRV_CHK_DISABLE))) { - /* server disabled */ - s->state |= SRV_GOINGDOWN; - s->proxy->lbprm.set_server_status_down(s); - - /* we might have sessions queued on this server and waiting for - * a connection. Those which are redispatchable will be queued - * to another server or to the proxy itself. - */ - xferred = redistribute_pending(s); - - sprintf(trash, - "Load-balancing on %sServer %s/%s is disabled. %d active and %d backup servers online.%s" - " %d sessions requeued, %d total in queue.\n", - s->state & SRV_BACKUP ? "Backup " : "", - s->proxy->id, s->id, s->proxy->srv_act, s->proxy->srv_bck, - (s->proxy->srv_bck && !s->proxy->srv_act) ? " Running on backup." : "", - xferred, s->nbpend); - - Warning("%s", trash); - - send_log(s->proxy, LOG_NOTICE, "%s", trash); - if (!s->proxy->srv_bck && !s->proxy->srv_act) - set_backend_down(s->proxy); - } + (SRV_CHK_RUNNING | SRV_CHK_DISABLE))) + set_server_disabled(s); } if (s->health < s->rise + s->fall - 1) { s->health++; /* was bad, stays for a while */ - if (s->health == s->rise) { - if (s->proxy->srv_bck == 0 && s->proxy->srv_act == 0) { - if (s->proxy->last_change < now.tv_sec) // ignore negative times - s->proxy->down_time += now.tv_sec - s->proxy->last_change; - s->proxy->last_change = now.tv_sec; - } - - if (s->last_change < now.tv_sec) // ignore negative times - s->down_time += now.tv_sec - s->last_change; - - s->last_change = now.tv_sec; - s->state |= SRV_RUNNING; - if (s->slowstart > 0) { - s->state |= SRV_WARMINGUP; - if (s->proxy->lbprm.algo & BE_LB_PROP_DYN) { - /* For dynamic algorithms, start at the first step of the weight, - * without multiplying by BE_WEIGHT_SCALE. - */ - s->eweight = s->uweight; - if (s->proxy->lbprm.update_server_eweight) - s->proxy->lbprm.update_server_eweight(s); - } - } - s->proxy->lbprm.set_server_status_up(s); - - /* check if we can handle some connections queued at the proxy. We - * will take as many as we can handle. - */ - xferred = check_for_pending(s); - - sprintf(trash, - "%sServer %s/%s is UP. %d active and %d backup servers online.%s" - " %d sessions requeued, %d total in queue.\n", - s->state & SRV_BACKUP ? "Backup " : "", - s->proxy->id, s->id, s->proxy->srv_act, s->proxy->srv_bck, - (s->proxy->srv_bck && !s->proxy->srv_act) ? " Running on backup." : "", - xferred, s->nbpend); - - Warning("%s", trash); - send_log(s->proxy, LOG_NOTICE, "%s", trash); - } - - if (s->health >= s->rise) - s->health = s->rise + s->fall - 1; /* OK now */ + set_server_up(s); } s->curfd = -1; /* no check running anymore */ fd_delete(fd); diff --git a/src/dumpstats.c b/src/dumpstats.c index 59ee93b..9f8d21c 100644 --- a/src/dumpstats.c +++ b/src/dumpstats.c
@@ -171,7 +171,7 @@ int print_csv_header(struct chunk *msg, int size)
"wretr,wredis," "status,weight,act,bck," "chkfail,chkdown,lastchg,downtime,qlimit," - "pid,iid,sid,throttle,lbtot," + "pid,iid,sid,throttle,lbtot,tracked," "\n"); }
@@ -587,7 +587,7 @@ int stats_dump_http(struct session *s, struct uri_auth *uri, int flags)
int stats_dump_proxy(struct session *s, struct proxy *px, struct uri_auth *uri, int flags) { struct buffer *rep = s->rep; - struct server *sv; + struct server *sv, *svs; /* server and server-state, server-state=server or server->tracked */ struct chunk msg; msg.len = 0;
@@ -706,8 +706,8 @@ int stats_dump_proxy(struct session *s, struct proxy *px, struct uri_auth *uri,
"%s," /* rest of server: nothing */ ",,,,,,,," - /* pid, iid, sid, throttle, lbtot, */ - "%d,%d,0,,," + /* pid, iid, sid, throttle, lbtot, tracked*/ + "%d,%d,0,,,,," "\n", px->id, px->feconn, px->feconn_max, px->maxconn, px->cum_feconn,
@@ -734,20 +734,25 @@ int stats_dump_proxy(struct session *s, struct proxy *px, struct uri_auth *uri,
sv = s->data_ctx.stats.sv; + if (sv->tracked) + svs = sv->tracked; + else + svs = sv; + /* FIXME: produce some small strings for "UP/DOWN x/y &#xxxx;" */ - if (!(sv->state & SRV_CHECKED)) + if (!(svs->state & SRV_CHECKED)) sv_state = 6; - else if (sv->state & SRV_RUNNING) { - if (sv->health == sv->rise + sv->fall - 1) + else if (svs->state & SRV_RUNNING) { + if (svs->health == svs->rise + svs->fall - 1) sv_state = 3; /* UP */ else sv_state = 2; /* going down */ - if (sv->state & SRV_GOINGDOWN) + if (svs->state & SRV_GOINGDOWN) sv_state += 2; } else - if (sv->health) + if (svs->health) sv_state = 1; /* going up */ else sv_state = 0; /* DOWN */
@@ -800,8 +805,8 @@ int stats_dump_proxy(struct session *s, struct proxy *px, struct uri_auth *uri,
chunk_printf(&msg, sizeof(trash), srv_hlt_st[sv_state], - (sv->state & SRV_RUNNING) ? (sv->health - sv->rise + 1) : (sv->health), - (sv->state & SRV_RUNNING) ? (sv->fall) : (sv->rise)); + (svs->state & SRV_RUNNING) ? (svs->health - svs->rise + 1) : (svs->health), + (svs->state & SRV_RUNNING) ? (svs->fall) : (svs->rise)); chunk_printf(&msg, sizeof(trash), /* weight */
@@ -819,8 +824,11 @@ int stats_dump_proxy(struct session *s, struct proxy *px, struct uri_auth *uri,
"<td align=right>%d</td><td align=right>%d</td>" "<td nowrap align=right>%s</td>" "", - sv->failed_checks, sv->down_trans, + svs->failed_checks, svs->down_trans, human_time(srv_downtime(sv), 1)); + else if (sv != svs) + chunk_printf(&msg, sizeof(trash), + "<td nowrap colspan=3>via %s/%s</td>", svs->proxy->id, svs->id ); else chunk_printf(&msg, sizeof(trash), "<td colspan=3></td>");
@@ -908,6 +916,14 @@ int stats_dump_proxy(struct session *s, struct proxy *px, struct uri_auth *uri,
/* sessions: lbtot */ chunk_printf(&msg, sizeof(trash), ",%d", sv->cum_lbconn); + + /* tracked */ + if (sv->tracked) + chunk_printf(&msg, sizeof(trash), ",%s/%s", + sv->tracked->proxy->id, sv->tracked->id); + else + chunk_printf(&msg, sizeof(trash), ","); + /* ',' then EOL */ chunk_printf(&msg, sizeof(trash), ",\n"); }
@@ -991,8 +1007,8 @@ int stats_dump_proxy(struct session *s, struct proxy *px, struct uri_auth *uri,
"%d,%d,%d," /* rest of backend: nothing, down transitions, last change, total downtime */ ",%d,%d,%d,," - /* pid, iid, sid, throttle, lbtot, */ - "%d,%d,0,,%d," + /* pid, iid, sid, throttle, lbtot, tracked,*/ + "%d,%d,0,,%d,," "\n", px->id, px->nbpend /* or px->totpend ? */, px->nbpend_max, diff --git a/src/proxy.c b/src/proxy.c index 281ee8e..d834589 100644 --- a/src/proxy.c +++ b/src/proxy.c
@@ -176,7 +176,7 @@ int proxy_parse_timeout(const char **args, struct proxy *proxy,
struct proxy *findproxy(const char *name, int mode, int cap) { - struct proxy *curproxy, *target=NULL; + struct proxy *curproxy, *target = NULL; for (curproxy = proxy; curproxy; curproxy = curproxy->next) { if ((curproxy->cap & cap)!=cap || strcmp(curproxy->id, name))
@@ -203,6 +203,31 @@ struct proxy *findproxy(const char *name, int mode, int cap) {
return target; } +struct server *findserver(const struct proxy *px, const char *name) { + + struct server *cursrv, *target = NULL; + + if (!px) + return NULL; + + for (cursrv = px->srv; cursrv; cursrv = cursrv->next) { + if (strcmp(cursrv->id, name)) + continue; + + if (!target) { + target = cursrv; + continue; + } + + Alert("Refusing to use duplicated server '%s' fould in proxy: %s!\n", + name, px->id); + + return NULL; + } + + return target; +} + /* * This function creates all proxy sockets. It should be done very early, * typically before privileges are dropped. The sockets will be registered -- 1.5.3.7
This archive was generated by hypermail 2.2.0 : 2008/02/05 21:45 CET