mirror of
https://git.haproxy.org/git/haproxy.git/
synced 2025-08-08 08:07:10 +02: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_embedded_ops;
|
||||||
extern struct si_ops si_conn_ops;
|
extern struct si_ops si_conn_ops;
|
||||||
extern struct data_cb si_conn_cb;
|
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);
|
struct appctx *stream_int_register_handler(struct stream_interface *si, struct si_applet *app);
|
||||||
void stream_int_unregister_handler(struct stream_interface *si);
|
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;
|
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
|
/* Attach connection <conn> to the stream interface <si>. The stream interface
|
||||||
* is configured to work with a connection and the connection it configured
|
* is configured to work with a connection and the connection it configured
|
||||||
* with a stream interface data layer.
|
* 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_read(s->rep);
|
||||||
channel_auto_close(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->req->analysers = s->listener->analysers;
|
||||||
s->rep->analysers = 0;
|
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_recv_cb(struct connection *conn);
|
||||||
static void si_conn_send_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_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 */
|
/* stream-interface operations for embedded tasks */
|
||||||
struct si_ops si_embedded_ops = {
|
struct si_ops si_embedded_ops = {
|
||||||
@ -74,6 +76,12 @@ struct data_cb si_conn_cb = {
|
|||||||
.wake = si_conn_wake_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
|
* 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
|
* 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;
|
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
|
/* 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
|
* 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
|
* events have been completed. It will also try to wake the associated task up if
|
||||||
|
Loading…
Reference in New Issue
Block a user