diff --git a/doc/configuration.txt b/doc/configuration.txt index becdab745..62fc5e902 100644 --- a/doc/configuration.txt +++ b/doc/configuration.txt @@ -448,6 +448,7 @@ The following keywords are supported in the "global" section : * Performance tuning - maxconn + - maxconnrate - maxpipes - noepoll - nokqueue @@ -649,6 +650,16 @@ maxconn connections when this limit is reached. The "ulimit-n" parameter is automatically adjusted according to this value. See also "ulimit-n". +maxconnrate + Sets the maximum per-process number of connections per second to . + Proxies will stop accepting connections when this limit is reached. It can be + used to limit the global capacity regardless of each frontend capacity. It is + important to note that this can only be used as a service protection measure, + as there will not necessarily be a fair share between frontends when the + limit is reached, so it's a good idea to also limit each frontend to some + value close to its expected share. Also, lowering tune.maxaccept can improve + fairness. + maxpipes Sets the maximum per-process number of pipes to . Currently, pipes are only used by kernel-based tcp splicing. Since a pipe contains two file diff --git a/include/proto/proxy.h b/include/proto/proxy.h index 2b5328c68..f5303bc72 100644 --- a/include/proto/proxy.h +++ b/include/proto/proxy.h @@ -25,6 +25,7 @@ #include #include #include +#include #include #include diff --git a/include/types/global.h b/include/types/global.h index cdd1bc478..a5c7fa01c 100644 --- a/include/types/global.h +++ b/include/types/global.h @@ -66,6 +66,8 @@ struct global { int gid; int nbproc; int maxconn, hardmaxconn; + struct freq_ctr conn_per_sec; + int cps_lim, cps_max; int maxpipes; /* max # of pipes */ int maxsock; /* max # of sockets */ int rlimit_nofile; /* default ulimit-n value : 0=unset */ diff --git a/src/cfgparse.c b/src/cfgparse.c index 5b942e2ea..1889654d2 100644 --- a/src/cfgparse.c +++ b/src/cfgparse.c @@ -682,6 +682,19 @@ int cfg_parse_global(const char *file, int linenum, char **args, int kwm) } #endif /* SYSTEM_MAXCONN */ } + else if (!strcmp(args[0], "maxconnrate")) { + if (global.cps_lim != 0) { + Alert("parsing [%s:%d] : '%s' already specified. Continuing.\n", file, linenum, args[0]); + err_code |= ERR_ALERT; + goto out; + } + if (*(args[1]) == 0) { + Alert("parsing [%s:%d] : '%s' expects an integer argument.\n", file, linenum, args[0]); + err_code |= ERR_ALERT | ERR_FATAL; + goto out; + } + global.cps_lim = atol(args[1]); + } else if (!strcmp(args[0], "maxpipes")) { if (global.maxpipes != 0) { Alert("parsing [%s:%d] : '%s' already specified. Continuing.\n", file, linenum, args[0]); diff --git a/src/dumpstats.c b/src/dumpstats.c index c9c40557a..4fde2919a 100644 --- a/src/dumpstats.c +++ b/src/dumpstats.c @@ -808,6 +808,7 @@ static int stats_sock_parse_request(struct stream_interface *si, char *line) } } + global.cps_max = 0; return 1; } else if (strcmp(args[1], "table") == 0) { diff --git a/src/stream_sock.c b/src/stream_sock.c index 11e74fdbb..365f52b97 100644 --- a/src/stream_sock.c +++ b/src/stream_sock.c @@ -1156,6 +1156,20 @@ int stream_sock_accept(int fd) return 0; } + if (global.cps_lim) { + int max = freq_ctr_remain(&global.conn_per_sec, global.cps_lim, 0); + + if (unlikely(!max)) { + /* frontend accept rate limit was reached */ + limit_listener(l, &global_listener_queue); + task_schedule(global_listener_queue_task, tick_add(now_ms, next_event_delay(&global.conn_per_sec, global.cps_lim, 0))); + return 0; + } + + if (max_accept > max) + max_accept = max; + } + if (p && p->fe_sps_lim) { int max = freq_ctr_remain(&p->fe_sess_per_sec, p->fe_sps_lim, 0); @@ -1237,6 +1251,11 @@ int stream_sock_accept(int fd) return 0; } + /* increase the per-process number of cumulated connections */ + update_freq_ctr(&global.conn_per_sec, 1); + if (global.conn_per_sec.curr_ctr > global.cps_max) + global.cps_max = global.conn_per_sec.curr_ctr; + jobs++; actconn++; totalconn++;