diff --git a/doc/haproxy-en.txt b/doc/haproxy-en.txt index 09d311e52..3e8ce107b 100644 --- a/doc/haproxy-en.txt +++ b/doc/haproxy-en.txt @@ -1364,6 +1364,21 @@ simultaneous sessions when the proxy instance will reach 10000 sessions, and will receive only 10 simultaneous sessions when the proxy will be under 1000 sessions. +It is possible to limit server queue length in order to rebalance excess +sessions between less busy application servers IF session affinity isn't +hard functional requirement (for example it just gives huge performance boost +by keeping server-local caches hot and compact). 'maxqueue' option sets a +queue limit on a server, as in example below: + +... (just the same as in example above) + server pentium3-800 192.168.1.1:80 cookie s1 weight 8 minconn 10 maxconn 100 check maxqueue 50 + server opteron-2.0G 192.168.1.2:80 cookie s2 weight 20 minconn 30 maxconn 300 check maxqueue 200 + server opteron-2.4G 192.168.1.3:80 cookie s3 weight 24 minconn 30 maxconn 300 check + +Absence of 'maxqueue' option means unlimited queue. When queue gets filled +up to 'maxqueue' client session is moved from server-local queue to a global +one. + Notes : ------- - The requests will not stay indefinitely in the queue, they follow the diff --git a/doc/haproxy-fr.txt b/doc/haproxy-fr.txt index d29259fc7..7974546bf 100644 --- a/doc/haproxy-fr.txt +++ b/doc/haproxy-fr.txt @@ -1391,6 +1391,23 @@ connexions simultan recevra seulement 10 connexions simultanées tant que le proxy sera sous les 1000 sessions. +Il est possible de limiter la taille de la file d'attente dans le but de +redistribuer les connexions destinées à un serveur en particulier qui sont trop +loin pour avoir une chance d'être servies en un temps raisonnable. Ceci n'est +acceptable que dans le cas où l'affinité entre le client et le serveur n'est +pas obligatoire, mais motivée uniquement par des raisons de performances, par +exemple, par l'utilisation d'un cache local au serveur. L'option 'maxqueue' +permet de préciser la limite par serveur, tel que dans l'exemple ci-dessous : + +... (même exemple que précédemment) + server pentium3-800 192.168.1.1:80 cookie s1 weight 8 minconn 10 maxconn 100 check maxqueue 50 + server opteron-2.0G 192.168.1.2:80 cookie s2 weight 20 minconn 30 maxconn 300 check maxqueue 200 + server opteron-2.4G 192.168.1.3:80 cookie s3 weight 24 minconn 30 maxconn 300 check + +En l'absence du paramètre 'maxqueue', la file d'un serveur n'a pas de limite +définie. Dans le cas contraire, lorsque la file atteint la limite fixée par +'maxqueue', les clients sont déplacés vers la file globale. + Notes : ------- - la requête ne restera pas indéfiniment en file d'attente, elle est diff --git a/include/types/server.h b/include/types/server.h index 62179f33d..6e3a9138b 100644 --- a/include/types/server.h +++ b/include/types/server.h @@ -64,6 +64,7 @@ struct server { int cur_sess, cur_sess_max; /* number of currently active sessions (including syn_sent) */ unsigned maxconn, minconn; /* max # of active sessions (0 = unlimited), min# for dynamic limit. */ int nbpend, nbpend_max; /* number of pending connections */ + int maxqueue; /* maximum number of pending connections allowed */ struct list pendconns; /* pending connections */ struct task *queue_mgt; /* the task associated to the queue processing */ diff --git a/src/backend.c b/src/backend.c index 596d5676c..faf5db89a 100644 --- a/src/backend.c +++ b/src/backend.c @@ -294,19 +294,25 @@ int assign_server_and_queue(struct session *s) return SRV_STATUS_INTERNAL; if (s->flags & SN_ASSIGNED) { - /* a server does not need to be assigned, perhaps because we're in - * direct mode, or in dispatch or transparent modes where the server - * is not needed. - */ - if (s->srv && - s->srv->maxconn && s->srv->cur_sess >= srv_dynamic_maxconn(s->srv)) { - p = pendconn_add(s); - if (p) - return SRV_STATUS_QUEUED; - else - return SRV_STATUS_FULL; + if (s->srv && s->srv->maxqueue > 0 && s->srv->nbpend >= s->srv->maxqueue) { + s->flags &= ~(SN_DIRECT | SN_ASSIGNED | SN_ADDR_SET); + s->srv = NULL; + http_flush_cookie_flags(&s->txn); + } else { + /* a server does not need to be assigned, perhaps because we're in + * direct mode, or in dispatch or transparent modes where the server + * is not needed. + */ + if (s->srv && + s->srv->maxconn && s->srv->cur_sess >= srv_dynamic_maxconn(s->srv)) { + p = pendconn_add(s); + if (p) + return SRV_STATUS_QUEUED; + else + return SRV_STATUS_FULL; + } + return SRV_STATUS_OK; } - return SRV_STATUS_OK; } /* a server needs to be assigned */ diff --git a/src/cfgparse.c b/src/cfgparse.c index 7b73e0bee..89c8c1997 100644 --- a/src/cfgparse.c +++ b/src/cfgparse.c @@ -1413,6 +1413,7 @@ int cfg_parse_listen(const char *file, int linenum, char **args) newsrv->fall = DEF_FALLTIME; newsrv->health = newsrv->rise; /* up, but will fall down at first failure */ newsrv->uweight = 1; + newsrv->maxqueue = 0; cur_arg = 3; while (*args[cur_arg]) { @@ -1465,6 +1466,10 @@ int cfg_parse_listen(const char *file, int linenum, char **args) newsrv->maxconn = atol(args[cur_arg + 1]); cur_arg += 2; } + else if (!strcmp(args[cur_arg], "maxqueue")) { + newsrv->maxqueue = atol(args[cur_arg + 1]); + cur_arg += 2; + } else if (!strcmp(args[cur_arg], "check")) { global.maxsock++; do_check = 1; @@ -1521,7 +1526,7 @@ int cfg_parse_listen(const char *file, int linenum, char **args) } #endif else { - Alert("parsing [%s:%d] : server %s only supports options 'backup', 'cookie', 'check', 'inter', 'rise', 'fall', 'addr', 'port', 'source', 'minconn', 'maxconn' and 'weight'.\n", + Alert("parsing [%s:%d] : server %s only supports options 'backup', 'cookie', 'check', 'inter', 'rise', 'fall', 'addr', 'port', 'source', 'minconn', 'maxconn', 'maxqueue', and 'weight'.\n", file, linenum, newsrv->id); return -1; } diff --git a/src/dumpstats.c b/src/dumpstats.c index 7ec4b565b..4d34c474a 100644 --- a/src/dumpstats.c +++ b/src/dumpstats.c @@ -372,7 +372,7 @@ int stats_dump_http(struct session *s, struct uri_auth *uri, int flags) "ereq,econ,eresp," "wretr,wredis," "status,weight,act,bck," - "chkfail,chkdown,lastchg,downtime," + "chkfail,chkdown,lastchg,downtime,qlimit," "\n"); } if (buffer_write_chunk(rep, &msg) != 0) @@ -582,20 +582,20 @@ int stats_dump_proxy(struct session *s, struct proxy *px, struct uri_auth *uri, if (flags & STAT_FMT_HTML) { /* print a new table */ chunk_printf(&msg, sizeof(trash), - "
| %s | " "" " | ||||||||||||||||||||||||||||
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
| " - " | Queue | Sessions | " + "Queue | Sessions | " "Bytes | Denied | " "Errors | Warnings | " "Server | " "||||||||||||||||||||
| Cur | Max | Cur | Max | " + "Cur | Max | Limit | Cur | Max | " "Limit | Cumul | In | Out | " "Req | Resp | Req | Conn | " "Resp | Retr | Redis | " @@ -617,7 +617,7 @@ int stats_dump_proxy(struct session *s, struct proxy *px, struct uri_auth *uri, if (flags & STAT_FMT_HTML) { chunk_printf(&msg, sizeof(trash), /* name, queue */ - "||||||||||
| Frontend | " + " | ||||||||||||||||||||||||||||
| Frontend | " /* sessions : current, max, limit, cumul */ " | %d | %d | " "%d | %d | " @@ -657,7 +657,7 @@ int stats_dump_proxy(struct session *s, struct proxy *px, struct uri_auth *uri, /* server status : reflect frontend status */ "%s," /* rest of server: nothing */ - ",,,,,,," + ",,,,,,,," "\n", px->id, px->feconn, px->feconn_max, px->maxconn, px->cum_feconn, @@ -709,8 +709,8 @@ int stats_dump_proxy(struct session *s, struct proxy *px, struct uri_auth *uri, chunk_printf(&msg, sizeof(trash), /* name */ "||||||||||||||||||||||||
| %s | " - /* queue : current, max */ - "%d | %d | " + /* queue : current, max, limit */ + "%d | %d | %s | " /* sessions : current, max, limit, cumul */ "%d | %d | " "%s | %d | " @@ -725,8 +725,8 @@ int stats_dump_proxy(struct session *s, struct proxy *px, struct uri_auth *uri, "", (sv->state & SRV_BACKUP) ? "backup" : "active", sv_state, sv->id, - sv->nbpend, sv->nbpend_max, - sv->cur_sess, sv->cur_sess_max, sv->maxconn ? ultoa(sv->maxconn) : "-", sv->cum_sess, + sv->nbpend, sv->nbpend_max, LIM2A0(sv->maxqueue, "-"), + sv->cur_sess, sv->cur_sess_max, LIM2A1(sv->maxconn, "-"), sv->cum_sess, sv->bytes_in, sv->bytes_out, sv->failed_secu, sv->failed_conns, sv->failed_resp, @@ -809,12 +809,17 @@ int stats_dump_proxy(struct session *s, struct proxy *px, struct uri_auth *uri, /* check failures: unique, fatal; last change, total downtime */ if (sv->state & SRV_CHECKED) chunk_printf(&msg, sizeof(trash), - "%d,%d,%d,%d,\n", + "%d,%d,%d,%d,", sv->failed_checks, sv->down_trans, now.tv_sec - sv->last_change, srv_downtime(sv)); else chunk_printf(&msg, sizeof(trash), - ",,,,\n"); + ",,,,"); + + /* queue limit and EOL */ + chunk_printf(&msg, sizeof(trash), + "%s,\n", + LIM2A0(sv->maxqueue, "")); } if (buffer_write_chunk(rep, &msg) != 0) return 0; @@ -845,7 +850,7 @@ int stats_dump_proxy(struct session *s, struct proxy *px, struct uri_auth *uri, /* name */ "||||||||||||||||||||
| Backend | " /* queue : current, max */ - "%d | %d | " + "%d | %d | " /* sessions : current, max, limit, cumul. */ " | %d | %d | %d | %d | " /* bytes : in, out */ @@ -903,7 +908,7 @@ int stats_dump_proxy(struct session *s, struct proxy *px, struct uri_auth *uri, "%d,%d,%d," /* rest of backend: nothing, down transformations, * last change, total downtime. */ - ",%d,%d,%d," + ",%d,%d,%d,," "\n", px->id, px->nbpend /* or px->totpend ? */, px->nbpend_max,||||||||||||||||||||