[MINOR] listeners: add support for queueing resource limited listeners

When a listeners encounters a resource shortage, it currently stops until
one re-enables it. This is far from being perfect as it does not yet handle
the case where the single connection from the listener is rejected (eg: the
stats page).

Now we'll have a special status for resource limited listeners and we'll
queue them into one or multiple lists. That way, each time we have to stop
a listener because of a resource shortage, we can enqueue it and change its
state, so that it is dequeued once more resources are available.

This patch currently does not change any existing behaviour, it only adds
the basic building blocks for doing that.
This commit is contained in:
Willy Tarreau 2011-07-24 22:03:52 +02:00
parent 627937158f
commit e6ca1fcd84
3 changed files with 59 additions and 4 deletions

View File

@ -71,6 +71,14 @@ int enable_all_listeners(struct protocol *proto);
*/
int disable_all_listeners(struct protocol *proto);
/* Marks a ready listener as limited so that we only try to re-enable it when
* resources are free again. It will be queued into the specified queue.
*/
void limit_listener(struct listener *l, struct list *list);
/* Dequeues all of the listeners waiting for a resource in wait queue <queue>. */
void dequeue_all_listeners(struct list *list);
/* This function closes the listening socket for the specified listener,
* provided that it's already in a listening state. The listener enters the
* LI_ASSIGNED state. It always returns ERR_NONE. This function is intended

View File

@ -46,6 +46,7 @@ enum {
LI_LISTEN, /* started, listening but not enabled */
LI_READY, /* started, listening and enabled */
LI_FULL, /* reached its connection limit */
LI_LIMITED, /* transient state: limits have been reached, listener is queued */
};
/* Listener transitions
@ -67,6 +68,11 @@ enum {
* +-----------------
* disable()
*
* The LIMITED state my be used when a limit has been detected just before
* using a listener. In this case, the listener MUST be queued into the
* appropriate wait queue (either the proxy's or the global one). It may be
* set back to the READY state at any instant and for any reason, so one must
* not rely on this state.
*/
/* listener socket options */
@ -101,6 +107,7 @@ struct listener {
struct task * (*handler)(struct task *t); /* protocol handler. It is a task */
int *timeout; /* pointer to client-side timeout */
struct proxy *frontend; /* the frontend this listener belongs to, or NULL */
struct list wait_queue; /* link element to make the listener wait for something (LI_LIMITED) */
unsigned int analysers; /* bitmap of required protocol analysers */
int nice; /* nice value to assign to the instanciated tasks */
union { /* protocol-dependant access restrictions */

View File

@ -50,6 +50,8 @@ void disable_listener(struct listener *listener)
return;
if (listener->state == LI_READY)
EV_FD_CLR(listener->fd, DIR_RD);
if (listener->state == LI_LIMITED)
LIST_DEL(&listener->wait_queue);
listener->state = LI_LISTEN;
}
@ -74,15 +76,18 @@ int pause_listener(struct listener *l)
if (shutdown(l->fd, SHUT_RD) != 0)
return 0; /* should always be OK */
if (l->state == LI_LIMITED)
LIST_DEL(&l->wait_queue);
EV_FD_CLR(l->fd, DIR_RD);
l->state = LI_PAUSED;
return 1;
}
/* This function tries to resume a temporarily disabled listener. Paused, full
* and disabled listeners are handled, which means that this function may
* replace enable_listener(). The resulting state will either be LI_READY or
* LI_FULL. 0 is returned in case of failure to resume (eg: dead socket).
/* This function tries to resume a temporarily disabled listener. Paused, full,
* limited and disabled listeners are handled, which means that this function
* may replace enable_listener(). The resulting state will either be LI_READY
* or LI_FULL. 0 is returned in case of failure to resume (eg: dead socket).
*/
int resume_listener(struct listener *l)
{
@ -96,6 +101,9 @@ int resume_listener(struct listener *l)
if (l->state == LI_READY)
return 1;
if (l->state == LI_LIMITED)
LIST_DEL(&l->wait_queue);
if (l->nbconn >= l->maxconn) {
l->state = LI_FULL;
return 1;
@ -112,11 +120,26 @@ int resume_listener(struct listener *l)
void listener_full(struct listener *l)
{
if (l->state >= LI_READY) {
if (l->state == LI_LIMITED)
LIST_DEL(&l->wait_queue);
EV_FD_CLR(l->fd, DIR_RD);
l->state = LI_FULL;
}
}
/* Marks a ready listener as limited so that we only try to re-enable it when
* resources are free again. It will be queued into the specified queue.
*/
void limit_listener(struct listener *l, struct list *list)
{
if (l->state == LI_READY) {
LIST_ADDQ(list, &l->wait_queue);
EV_FD_CLR(l->fd, DIR_RD);
l->state = LI_LIMITED;
}
}
/* This function adds all of the protocol's listener's file descriptors to the
* polling lists when they are in the LI_LISTEN state. It is intended to be
* used as a protocol's generic enable_all() primitive, for use after the
@ -146,6 +169,20 @@ int disable_all_listeners(struct protocol *proto)
return ERR_NONE;
}
/* Dequeues all of the listeners waiting for a resource in wait queue <queue>. */
void dequeue_all_listeners(struct list *list)
{
struct listener *listener, *l_back;
list_for_each_entry_safe(listener, l_back, list, wait_queue) {
/* This cannot fail because the listeners are by definition in
* the LI_LIMITED state. The function also removes the entry
* from the queue.
*/
resume_listener(listener);
}
}
/* This function closes the listening socket for the specified listener,
* provided that it's already in a listening state. The listener enters the
* LI_ASSIGNED state. It always returns ERR_NONE. This function is intended
@ -156,6 +193,9 @@ int unbind_listener(struct listener *listener)
if (listener->state == LI_READY)
EV_FD_CLR(listener->fd, DIR_RD);
if (listener->state == LI_LIMITED)
LIST_DEL(&listener->wait_queue);
if (listener->state >= LI_PAUSED) {
fd_delete(listener->fd);
listener->state = LI_ASSIGNED;