MEDIUM: global: add a 'hard-stop-after' option to cap the soft-stop time

When SIGUSR1 is received, haproxy enters in soft-stop and quits when no
connection remains.
It can happen that the instance remains alive for a long time, depending
on timeouts and traffic. This option ensures that soft-stop won't run
for too long.

Example:
  global
    hard-stop-after 30s  # Once in soft-stop, the instance will remain
                         # alive for at most 30 seconds.
This commit is contained in:
Cyril Bont 2017-03-23 22:44:13 +01:00 committed by Willy Tarreau
parent 1a943c48fb
commit 203ec5a2b5
4 changed files with 88 additions and 1 deletions

View file

@ -536,6 +536,7 @@ The following keywords are supported in the "global" section :
- external-check
- gid
- group
- hard-stop-after
- log
- log-tag
- log-send-hostname
@ -703,6 +704,22 @@ gid <number>
will only be able to drop these groups if started with superuser privileges.
See also "group" and "uid".
hard-stop-after <time>
Defines the maximum time allowed to perform a clean soft-stop.
Arguments :
<time> is the maximum time (by default in milliseconds) for which the
instance will remain alive when a soft-stop is received via the
SIGUSR1 signal.
This may be used to ensure that the instance will quit even if connections
remain opened during a soft-stop (for example with long timeouts for a proxy
in tcp mode). It applies both in TCP and HTTP mode.
Example:
global
hard-stop-after 30s
group <group name>
Similar to "gid" but uses the GID of group name <group name> from /etc/group.
See also "gid" and "user".

View file

@ -80,6 +80,7 @@ struct global {
int gid;
int external_check;
int nbproc;
unsigned int hard_stop_after; /* maximum time allowed to perform a soft-stop */
int maxconn, hardmaxconn;
int maxsslconn;
int ssl_session_max_cost; /* how many bytes an SSL session may cost */
@ -170,6 +171,7 @@ extern const int zero;
extern const int one;
extern const struct linger nolinger;
extern int stopping; /* non zero means stopping in progress */
extern int killed; /* non zero means a hard-stop is triggered */
extern char hostname[MAX_HOSTNAME_LEN];
extern char localpeer[MAX_HOSTNAME_LEN];
extern struct list global_listener_queue; /* list of the temporarily limited listeners */
@ -194,6 +196,7 @@ static inline int already_warned(unsigned int warning)
return 0;
}
void deinit(void);
void hap_register_build_opts(const char *str, int must_free);
void hap_register_post_check(int (*fct)());
void hap_register_post_deinit(void (*fct)());

View file

@ -117,6 +117,7 @@ int relative_pid = 1; /* process id starting at 1 */
/* global options */
struct global global = {
.hard_stop_after = TICK_ETERNITY,
.nbproc = 1,
.req_count = 0,
.logsrvs = LIST_HEAD_INIT(global.logsrvs),
@ -157,6 +158,7 @@ struct global global = {
/*********************************************************************/
int stopping; /* non zero means stopping in progress */
int killed; /* non zero means a hard-stop is triggered */
int jobs = 0; /* number of active jobs (conns, listeners, active tasks, ...) */
/* Here we store informations about the pids of the processes we may pause
@ -593,6 +595,7 @@ static void init(int argc, char **argv)
*/
totalconn = actconn = maxfd = listeners = stopping = 0;
killed = 0;
#ifdef HAPROXY_MEMMAX
@ -1225,7 +1228,7 @@ static void deinit_stick_rules(struct list *rules)
}
}
static void deinit(void)
void deinit(void)
{
struct proxy *p = proxy, *p0;
struct cap_hdr *h,*h_next;

View file

@ -914,6 +914,58 @@ struct task *manage_proxy(struct task *t)
}
static int proxy_parse_hard_stop_after(char **args, int section_type, struct proxy *curpx,
struct proxy *defpx, const char *file, int line,
char **err)
{
const char *res;
if (!*args[1]) {
memprintf(err, "'%s' expects <time> as argument.\n", args[0]);
return -1;
}
res = parse_time_err(args[1], &global.hard_stop_after, TIME_UNIT_MS);
if (res) {
memprintf(err, "unexpected character '%c' in argument to <%s>.\n", *res, args[0]);
return -1;
}
return 0;
}
struct task *hard_stop(struct task *t)
{
struct proxy *p;
struct stream *s;
if (killed) {
Warning("Some tasks resisted to hard-stop, exiting now.\n");
send_log(NULL, LOG_WARNING, "Some tasks resisted to hard-stop, exiting now.\n");
/* Do some cleanup and explicitely quit */
deinit();
exit(0);
}
Warning("soft-stop running for too long, performing a hard-stop.\n");
send_log(NULL, LOG_WARNING, "soft-stop running for too long, performing a hard-stop.\n");
p = proxy;
while (p) {
if ((p->cap & PR_CAP_FE) && (p->feconn > 0)) {
Warning("Proxy %s hard-stopped (%d remaining conns will be closed).\n",
p->id, p->feconn);
send_log(p, LOG_WARNING, "Proxy %s hard-stopped (%d remaining conns will be closed).\n",
p->id, p->feconn);
}
p = p->next;
}
list_for_each_entry(s, &streams, list) {
stream_shutdown(s, SF_ERR_KILLED);
}
killed = 1;
t->expire = tick_add(now_ms, MS_TO_TICKS(1000));
return t;
}
/*
* this function disables health-check servers so that the process will quickly be ignored
* by load balancers. Note that if a proxy was already in the PAUSED state, then its grace
@ -923,8 +975,19 @@ void soft_stop(void)
{
struct proxy *p;
struct peers *prs;
struct task *task;
stopping = 1;
if (tick_isset(global.hard_stop_after)) {
task = task_new();
if (task) {
task->process = hard_stop;
task_schedule(task, tick_add(now_ms, global.hard_stop_after));
}
else {
Alert("out of memory trying to allocate the hard-stop task.\n");
}
}
p = proxy;
tv_update_date(0,1); /* else, the old time before select will be used */
while (p) {
@ -1215,6 +1278,7 @@ int stream_set_backend(struct stream *s, struct proxy *be)
}
static struct cfg_kw_list cfg_kws = {ILH, {
{ CFG_GLOBAL, "hard-stop-after", proxy_parse_hard_stop_after },
{ CFG_LISTEN, "timeout", proxy_parse_timeout },
{ CFG_LISTEN, "clitimeout", proxy_parse_timeout },
{ CFG_LISTEN, "contimeout", proxy_parse_timeout },