mirror of
http://git.haproxy.org/git/haproxy.git/
synced 2025-03-01 00:50:53 +00:00
MEDIUM: sessions: Don't keep an infinite number of idling connections.
In session, don't keep an infinite number of connection that can idle. Add a new frontend parameter, "max-session-srv-conns" to set a max number, with a default value of 5.
This commit is contained in:
parent
f502aca5c2
commit
a4d4fdfaa3
@ -5358,6 +5358,10 @@ max-keep-alive-queue <value>
|
||||
See also : "option http-server-close", "option prefer-last-server", server
|
||||
"maxconn" and cookie persistence.
|
||||
|
||||
max-session-srv-conns <nb>
|
||||
Set the maximum number of outgoing connections we can keep idling for a given
|
||||
client session. The default is 5 (it precisely equals MAX_SRV_LIST which is
|
||||
defined at build time).
|
||||
|
||||
maxconn <conns>
|
||||
Fix the maximum number of concurrent connections on a frontend
|
||||
|
@ -664,7 +664,11 @@ static inline void conn_force_unsubscribe(struct connection *conn)
|
||||
static inline void conn_free(struct connection *conn)
|
||||
{
|
||||
/* Remove ourself from the session's connections list, if any. */
|
||||
LIST_DEL(&conn->session_list);
|
||||
if (!LIST_ISEMPTY(&conn->session_list)) {
|
||||
struct session *sess = conn->owner;
|
||||
sess->resp_conns--;
|
||||
LIST_DEL(&conn->session_list);
|
||||
}
|
||||
/* If we temporarily stored the connection as the stream_interface's
|
||||
* end point, remove it.
|
||||
*/
|
||||
|
@ -233,6 +233,27 @@ static inline enum srv_initaddr srv_get_next_initaddr(unsigned int *list)
|
||||
return ret;
|
||||
}
|
||||
|
||||
static inline int srv_add_to_idle_list(struct server *srv, struct connection *conn)
|
||||
{
|
||||
if (srv && srv->pool_purge_delay > 0 && (srv->max_idle_conns == -1 ||
|
||||
srv->max_idle_conns > srv->curr_idle_conns) &&
|
||||
!(conn->flags & CO_FL_PRIVATE) &&
|
||||
((srv->proxy->options & PR_O_REUSE_MASK) != PR_O_REUSE_NEVR) &&
|
||||
conn->mux->avail_streams(conn) == conn->mux->max_streams(conn)) {
|
||||
LIST_DEL(&conn->list);
|
||||
LIST_ADDQ(&srv->idle_orphan_conns[tid], &conn->list);
|
||||
srv->curr_idle_conns++;
|
||||
|
||||
conn->idle_time = now_ms;
|
||||
if (!(task_in_wq(srv->idle_task[tid])) &&
|
||||
!(task_in_rq(srv->idle_task[tid])))
|
||||
task_schedule(srv->idle_task[tid],
|
||||
tick_add(now_ms, srv->pool_purge_delay));
|
||||
return 1;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
#endif /* _PROTO_SERVER_H */
|
||||
|
||||
/*
|
||||
|
@ -30,7 +30,9 @@
|
||||
#include <types/global.h>
|
||||
#include <types/session.h>
|
||||
|
||||
#include <proto/obj_type.h>
|
||||
#include <proto/stick_table.h>
|
||||
#include <proto/server.h>
|
||||
|
||||
extern struct pool_head *pool_head_session;
|
||||
struct session *session_new(struct proxy *fe, struct listener *li, enum obj_type *origin);
|
||||
@ -76,6 +78,7 @@ static inline void session_add_conn(struct session *sess, struct connection *con
|
||||
int avail = -1;
|
||||
int i;
|
||||
|
||||
sess->resp_conns++;
|
||||
for (i = 0; i < MAX_SRV_LIST; i++) {
|
||||
if (sess->srv_list[i].target == target) {
|
||||
avail = i;
|
||||
@ -99,6 +102,7 @@ static inline void session_add_conn(struct session *sess, struct connection *con
|
||||
}
|
||||
/* Now unown all the connections */
|
||||
list_for_each_entry_safe(conn, conn_back, &sess->srv_list[avail].list, session_list) {
|
||||
sess->resp_conns--;
|
||||
conn->owner = NULL;
|
||||
LIST_DEL(&conn->session_list);
|
||||
LIST_INIT(&conn->session_list);
|
||||
@ -111,6 +115,24 @@ static inline void session_add_conn(struct session *sess, struct connection *con
|
||||
LIST_ADDQ(&sess->srv_list[avail].list, &conn->session_list);
|
||||
}
|
||||
|
||||
/* Returns 0 if the session can keep the idle conn, -1 if it was destroyed, or 1 if it was added to the server list */
|
||||
static inline int session_check_idle_conn(struct session *sess, struct connection *conn)
|
||||
{
|
||||
if (sess->resp_conns > sess->fe->max_out_conns) {
|
||||
/* We can't keep the connection, let's try to add it to the server idle list */
|
||||
LIST_DEL(&conn->session_list);
|
||||
LIST_INIT(&conn->session_list);
|
||||
conn->owner = NULL;
|
||||
sess->resp_conns--;
|
||||
if (!srv_add_to_idle_list(objt_server(conn->target), conn)) {
|
||||
/* The server doesn't want it, let's kill the connection right away */
|
||||
conn->mux->destroy(conn);
|
||||
return -1;
|
||||
}
|
||||
return 1;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
#endif /* _PROTO_SESSION_H */
|
||||
|
||||
|
@ -278,6 +278,7 @@ struct proxy {
|
||||
|
||||
int options; /* PR_O_REDISP, PR_O_TRANSP, ... */
|
||||
int options2; /* PR_O2_* */
|
||||
int max_out_conns; /* Max number of idling connections we keep for a session */
|
||||
struct in_addr mon_net, mon_mask; /* don't forward connections from this net (network order) FIXME: should support IPv6 */
|
||||
unsigned int ck_opts; /* PR_CK_* (cookie options) */
|
||||
unsigned int fe_req_ana, be_req_ana; /* bitmap of common request protocol analysers for the frontend and backend */
|
||||
|
@ -54,6 +54,7 @@ struct session {
|
||||
struct vars vars; /* list of variables for the session scope. */
|
||||
struct task *task; /* handshake timeout processing */
|
||||
long t_handshake; /* handshake duration, -1 = not completed */
|
||||
int resp_conns; /* Number of connections we're currently responsible for */
|
||||
struct sess_srv_list srv_list[MAX_SRV_LIST]; /* List of servers and the connections the session is currently responsible for */
|
||||
};
|
||||
|
||||
|
@ -1254,8 +1254,9 @@ int connect_server(struct stream *s)
|
||||
(srv_conn->mux->avail_streams(srv_conn) == 1)) {
|
||||
LIST_DEL(&old_conn->session_list);
|
||||
LIST_INIT(&old_conn->session_list);
|
||||
session_add_conn(sess, old_conn, s->target);
|
||||
old_conn->owner = sess;
|
||||
session_add_conn(sess, old_conn, s->target);
|
||||
session_check_idle_conn(sess, old_conn);
|
||||
}
|
||||
}
|
||||
|
||||
@ -1283,6 +1284,7 @@ int connect_server(struct stream *s)
|
||||
if (srv_conn && old_conn != srv_conn) {
|
||||
srv_conn->owner = s->sess;
|
||||
LIST_DEL(&srv_conn->session_list);
|
||||
LIST_INIT(&srv_conn->session_list);
|
||||
session_add_conn(s->sess, srv_conn, s->target);
|
||||
}
|
||||
|
||||
|
@ -421,6 +421,7 @@ int cfg_parse_listen(const char *file, int linenum, char **args, int kwm)
|
||||
curproxy->fe_sps_lim = defproxy.fe_sps_lim;
|
||||
|
||||
curproxy->to_log = defproxy.to_log & ~LW_COOKIE & ~LW_REQHDR & ~ LW_RSPHDR;
|
||||
curproxy->max_out_conns = defproxy.max_out_conns;
|
||||
}
|
||||
|
||||
if (curproxy->cap & PR_CAP_BE) {
|
||||
@ -1327,6 +1328,17 @@ int cfg_parse_listen(const char *file, int linenum, char **args, int kwm)
|
||||
else
|
||||
curproxy->server_state_file_name = strdup(args[1]);
|
||||
}
|
||||
else if (!strcmp(args[0], "max-session-srv-conns")) {
|
||||
if (warnifnotcap(curproxy, PR_CAP_FE, file, linenum, args[0], NULL))
|
||||
err_code |= ERR_WARN;
|
||||
if (*(args[1]) == 0) {
|
||||
ha_alert("parsine [%s:%d] : '%s' expects a number. Got no argument\n",
|
||||
file, linenum, args[0]);
|
||||
err_code |= ERR_ALERT | ERR_FATAL;
|
||||
goto out;
|
||||
}
|
||||
curproxy->max_out_conns = atoi(args[1]);
|
||||
}
|
||||
else if (!strcmp(args[0], "capture")) {
|
||||
if (warnifnotcap(curproxy, PR_CAP_FE, file, linenum, args[0], NULL))
|
||||
err_code |= ERR_WARN;
|
||||
|
@ -455,6 +455,7 @@ void init_default_instance()
|
||||
defproxy.redispatch_after = 0;
|
||||
defproxy.lbprm.chash.balance_factor = 0;
|
||||
defproxy.options = PR_O_REUSE_SAFE;
|
||||
defproxy.max_out_conns = MAX_SRV_LIST;
|
||||
|
||||
defproxy.defsrv.check.inter = DEF_CHKINTR;
|
||||
defproxy.defsrv.check.fastinter = 0;
|
||||
|
13
src/mux_h1.c
13
src/mux_h1.c
@ -1947,6 +1947,19 @@ static void h1_detach(struct conn_stream *cs)
|
||||
h1c->conn->owner = sess;
|
||||
session_add_conn(sess, h1c->conn, h1c->conn->target);
|
||||
}
|
||||
if (h1c->conn->owner == sess) {
|
||||
int ret = session_check_idle_conn(sess, h1c->conn);
|
||||
if (ret == -1)
|
||||
/* The connection got destroyed, let's leave */
|
||||
return;
|
||||
else if (ret == 1) {
|
||||
/* The connection was added to the server list,
|
||||
* wake the task so we can subscribe to events
|
||||
*/
|
||||
tasklet_wakeup(h1c->wait_event.task);
|
||||
return;
|
||||
}
|
||||
}
|
||||
/* we're in keep-alive with an idle connection, monitor it if not already done */
|
||||
if (LIST_ISEMPTY(&h1c->conn->list)) {
|
||||
struct server *srv = objt_server(h1c->conn->target);
|
||||
|
@ -2850,6 +2850,11 @@ static void h2_detach(struct conn_stream *cs)
|
||||
h2c->conn->owner = sess;
|
||||
session_add_conn(sess, h2c->conn, h2c->conn->target);
|
||||
}
|
||||
if (eb_is_empty(&h2c->streams_by_id)) {
|
||||
if (session_check_idle_conn(h2c->conn->owner, h2c->conn) != 0)
|
||||
/* At this point either the connection is destroyed, or it's been added to the server idle list, just stop */
|
||||
return;
|
||||
}
|
||||
/* Never ever allow to reuse a connection from a non-reuse backend */
|
||||
if ((h2c->proxy->options & PR_O_REUSE_MASK) == PR_O_REUSE_NEVR)
|
||||
h2c->conn->flags |= CO_FL_PRIVATE;
|
||||
|
@ -3489,9 +3489,19 @@ void http_end_txn_clean_session(struct stream *s)
|
||||
srv_conn = NULL;
|
||||
else if (!srv_conn->owner) {
|
||||
srv_conn->owner = s->sess;
|
||||
/* Add it unconditionally to the session list, it'll be removed
|
||||
* later if needed by session_check_idle_conn(), once we'll
|
||||
* have released the endpoint and know if it no longer has
|
||||
* attached streams, and so an idling connection
|
||||
*/
|
||||
session_add_conn(s->sess, srv_conn, s->target);
|
||||
}
|
||||
si_release_endpoint(&s->si[1]);
|
||||
if (srv_conn && srv_conn->owner == s->sess) {
|
||||
if (session_check_idle_conn(s->sess, srv_conn) != 0)
|
||||
srv_conn = NULL;
|
||||
}
|
||||
|
||||
|
||||
s->si[1].state = s->si[1].prev_state = SI_ST_INI;
|
||||
s->si[1].err_type = SI_ET_NONE;
|
||||
|
@ -64,6 +64,7 @@ struct session *session_new(struct proxy *fe, struct listener *li, enum obj_type
|
||||
sess->srv_list[i].target = NULL;
|
||||
LIST_INIT(&sess->srv_list[i].list);
|
||||
}
|
||||
sess->resp_conns = 0;
|
||||
}
|
||||
return sess;
|
||||
}
|
||||
@ -86,30 +87,11 @@ void session_free(struct session *sess)
|
||||
list_for_each_entry_safe(conn, conn_back, &sess->srv_list[i].list, session_list) {
|
||||
count++;
|
||||
if (conn->mux) {
|
||||
struct server *srv;
|
||||
|
||||
LIST_DEL(&conn->session_list);
|
||||
LIST_INIT(&conn->session_list);
|
||||
srv = objt_server(conn->target);
|
||||
conn->owner = NULL;
|
||||
if (srv && srv->pool_purge_delay > 0 &&
|
||||
(srv->max_idle_conns == -1 ||
|
||||
srv->max_idle_conns > srv->curr_idle_conns) &&
|
||||
!(conn->flags & CO_FL_PRIVATE) &&
|
||||
conn->mux->avail_streams(conn) ==
|
||||
conn->mux->max_streams(conn)) {
|
||||
LIST_DEL(&conn->list);
|
||||
|
||||
LIST_ADDQ(&srv->idle_orphan_conns[tid],
|
||||
&conn->list);
|
||||
srv->curr_idle_conns++;
|
||||
|
||||
conn->idle_time = now_ms;
|
||||
if (!(task_in_wq(srv->idle_task[tid])) &&
|
||||
!(task_in_rq(srv->idle_task[tid])))
|
||||
task_schedule(srv->idle_task[tid],
|
||||
tick_add(now_ms, srv->pool_purge_delay));
|
||||
} else
|
||||
if (!srv_add_to_idle_list(objt_server(conn->target), conn))
|
||||
conn->mux->destroy(conn);
|
||||
} else {
|
||||
/* We have a connection, but not yet an associated mux.
|
||||
|
Loading…
Reference in New Issue
Block a user