]> git.ipfire.org Git - thirdparty/haproxy.git/commitdiff
[MEDIUM] add support for global.maxconnrate to limit the per-process conn rate.
authorWilly Tarreau <w@1wt.eu>
Wed, 7 Sep 2011 13:17:21 +0000 (15:17 +0200)
committerWilly Tarreau <w@1wt.eu>
Wed, 7 Sep 2011 20:47:42 +0000 (22:47 +0200)
This one enforces a per-process connection rate limit, regardless of what
may be set per frontend. It can be a way to limit the CPU usage of a process
being severely attacked.

The side effect is that the global process connection rate is now measured
for each incoming connection, so it will be possible to report it.

doc/configuration.txt
include/proto/proxy.h
include/types/global.h
src/cfgparse.c
src/dumpstats.c
src/stream_sock.c

index becdab745bff8ba0b977fec9bdd31ade6075fd99..62fc5e902ce0657c73325ef35075d7352c5fcaf2 100644 (file)
@@ -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 <number>
   connections when this limit is reached. The "ulimit-n" parameter is
   automatically adjusted according to this value. See also "ulimit-n".
 
+maxconnrate <number>
+  Sets the maximum per-process number of connections per second to <number>.
+  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 <number>
   Sets the maximum per-process number of pipes to <number>. Currently, pipes
   are only used by kernel-based tcp splicing. Since a pipe contains two file
index 2b5328c680bd0d687583b26b8d62be72c4de7644..f5303bc72e327c56b432722e0faa1a6007df1965 100644 (file)
@@ -25,6 +25,7 @@
 #include <common/config.h>
 #include <common/ticks.h>
 #include <common/time.h>
+#include <types/global.h>
 #include <types/proxy.h>
 #include <proto/freq_ctr.h>
 
index cdd1bc478e6b256eaf231b53a167b91ee92bbe87..a5c7fa01c37f748614b633d6fc12124c7b860991 100644 (file)
@@ -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 */
index 5b942e2ea94708dc537ea06b12f8241811466c7b..1889654d2605016540c2988fe3e7f5c53a1e86a2 100644 (file)
@@ -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]);
index c9c40557a08a21b42008faab7ba8f233fbf58758..4fde2919a95c324e196533139c498121b6fed8df 100644 (file)
@@ -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) {
index 11e74fdbb48b2f3e7a737c9b18081fdeab64772e..365f52b97f3fe6bb8021f5fab457fb1fff0de736 100644 (file)
@@ -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++;