mirror of
http://git.haproxy.org/git/haproxy.git/
synced 2025-03-01 17:10:42 +00:00
MEDIUM: stream-int: implement a very simplistic idle connection manager
Idle connections are not monitored right now. So if a server closes after a response without advertising it, it won't be detected until a next request wants to use the connection. This is a bit problematic because it unnecessarily maintains file descriptors and sockets in an idle state. This patch implements a very simple idle connection manager for the stream interface. It presents itself as an I/O callback. The HTTP engine enables it when it recycles a connection. If a close or an error is detected on the underlying socket, it tries to drain as much data as possible from the socket, detect the close and responds with a close as well, then detaches from the stream interface.
This commit is contained in:
parent
4bfa4228dc
commit
2737562e43
@ -41,6 +41,7 @@ void stream_sock_read0(struct stream_interface *si);
|
||||
extern struct si_ops si_embedded_ops;
|
||||
extern struct si_ops si_conn_ops;
|
||||
extern struct data_cb si_conn_cb;
|
||||
extern struct data_cb si_idle_conn_cb;
|
||||
|
||||
struct appctx *stream_int_register_handler(struct stream_interface *si, struct si_applet *app);
|
||||
void stream_int_unregister_handler(struct stream_interface *si);
|
||||
@ -138,6 +139,21 @@ static inline void si_detach(struct stream_interface *si)
|
||||
si->ops = &si_embedded_ops;
|
||||
}
|
||||
|
||||
/* Turn a possibly existing connection endpoint of stream interface <si> to
|
||||
* idle mode, which means that the connection will be polled for incoming events
|
||||
* and might be killed by the underlying I/O handler.
|
||||
*/
|
||||
static inline void si_idle_conn(struct stream_interface *si)
|
||||
{
|
||||
struct connection *conn = objt_conn(si->end);
|
||||
|
||||
if (!conn)
|
||||
return;
|
||||
|
||||
conn_attach(conn, si, &si_idle_conn_cb);
|
||||
conn_data_want_recv(conn);
|
||||
}
|
||||
|
||||
/* Attach connection <conn> to the stream interface <si>. The stream interface
|
||||
* is configured to work with a connection and the connection it configured
|
||||
* with a stream interface data layer.
|
||||
|
@ -4423,6 +4423,9 @@ void http_end_txn_clean_session(struct session *s)
|
||||
channel_auto_read(s->rep);
|
||||
channel_auto_close(s->rep);
|
||||
|
||||
/* we're in keep-alive with an idle connection, monitor it */
|
||||
si_idle_conn(s->req->cons);
|
||||
|
||||
s->req->analysers = s->listener->analysers;
|
||||
s->rep->analysers = 0;
|
||||
|
||||
|
@ -49,6 +49,8 @@ static void stream_int_chk_snd_conn(struct stream_interface *si);
|
||||
static void si_conn_recv_cb(struct connection *conn);
|
||||
static void si_conn_send_cb(struct connection *conn);
|
||||
static int si_conn_wake_cb(struct connection *conn);
|
||||
static int si_idle_conn_wake_cb(struct connection *conn);
|
||||
static void si_idle_conn_null_cb(struct connection *conn);
|
||||
|
||||
/* stream-interface operations for embedded tasks */
|
||||
struct si_ops si_embedded_ops = {
|
||||
@ -74,6 +76,12 @@ struct data_cb si_conn_cb = {
|
||||
.wake = si_conn_wake_cb,
|
||||
};
|
||||
|
||||
struct data_cb si_idle_conn_cb = {
|
||||
.recv = si_idle_conn_null_cb,
|
||||
.send = si_idle_conn_null_cb,
|
||||
.wake = si_idle_conn_wake_cb,
|
||||
};
|
||||
|
||||
/*
|
||||
* This function only has to be called once after a wakeup event in case of
|
||||
* suspected timeout. It controls the stream interface timeouts and sets
|
||||
@ -483,6 +491,47 @@ int conn_si_send_proxy(struct connection *conn, unsigned int flag)
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
/* Tiny I/O callback called on recv/send I/O events on idle connections.
|
||||
* It simply sets the CO_FL_SOCK_RD_SH flag so that si_idle_conn_wake_cb()
|
||||
* is notified and can kill the connection.
|
||||
*/
|
||||
static void si_idle_conn_null_cb(struct connection *conn)
|
||||
{
|
||||
if (conn->flags & (CO_FL_ERROR | CO_FL_SOCK_RD_SH))
|
||||
return;
|
||||
|
||||
if ((fdtab[conn->t.sock.fd].ev & (FD_POLL_ERR|FD_POLL_HUP)) ||
|
||||
(conn->ctrl->drain && conn->ctrl->drain(conn->t.sock.fd)))
|
||||
conn->flags |= CO_FL_SOCK_RD_SH;
|
||||
|
||||
/* disable draining if we were called and have no drain function */
|
||||
if (!conn->ctrl->drain)
|
||||
__conn_data_stop_recv(conn);
|
||||
}
|
||||
|
||||
/* Callback to be used by connection I/O handlers when some activity is detected
|
||||
* on an idle server connection. Its main purpose is to kill the connection once
|
||||
* a close was detected on it. It returns 0 if it did nothing serious, or -1 if
|
||||
* it killed the connection.
|
||||
*/
|
||||
static int si_idle_conn_wake_cb(struct connection *conn)
|
||||
{
|
||||
struct stream_interface *si = conn->owner;
|
||||
|
||||
if (!conn_ctrl_ready(conn))
|
||||
return 0;
|
||||
|
||||
if (conn->flags & (CO_FL_ERROR | CO_FL_SOCK_RD_SH)) {
|
||||
/* warning, we can't do anything on <conn> after this call ! */
|
||||
conn_force_close(conn);
|
||||
conn_free(conn);
|
||||
si->end = NULL;
|
||||
return -1;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* Callback to be used by connection I/O handlers upon completion. It differs from
|
||||
* the update function in that it is designed to be called by lower layers after I/O
|
||||
* events have been completed. It will also try to wake the associated task up if
|
||||
|
Loading…
Reference in New Issue
Block a user