[MAJOR] first limited implementation of connection queueing.
There is no timeout yet, and the server UP/DOWN events are not used to export/import list of connections yet. It seems that the process can sometimes eat lots of user CPU (~50%) if a maxconn is set on an overloaded server.
This commit is contained in:
parent
fd5c879b4d
commit
dfece23f7d
681
haproxy.c
681
haproxy.c
|
@ -405,6 +405,8 @@ int strlcpy2(char *dst, const char *src, int size) {
|
|||
|
||||
/* various other session flags, bits values 0x400000 and above */
|
||||
#define SN_MONITOR 0x00400000 /* this session comes from a monitoring system */
|
||||
#define SN_ASSIGNED 0x00800000 /* no need to assign a server to this session */
|
||||
#define SN_ADDR_SET 0x01000000 /* this session's server address has been set */
|
||||
|
||||
|
||||
/* different possible states for the client side */
|
||||
|
@ -448,6 +450,13 @@ int strlcpy2(char *dst, const char *src, int size) {
|
|||
#define SRV_BIND_SRC 8 /* this server uses a specific source address */
|
||||
#define SRV_CHECKED 16 /* this server needs to be checked */
|
||||
|
||||
/* function which act on servers need to return various errors */
|
||||
#define SRV_STATUS_OK 0 /* everything is OK. */
|
||||
#define SRV_STATUS_INTERNAL 1 /* other unrecoverable errors. */
|
||||
#define SRV_STATUS_NOSRV 2 /* no server is available */
|
||||
#define SRV_STATUS_FULL 3 /* the/all server(s) are saturated */
|
||||
#define SRV_STATUS_QUEUED 4 /* the/all server(s) are saturated but the connection was queued */
|
||||
|
||||
/* what to do when a header matches a regex */
|
||||
#define ACT_ALLOW 0 /* allow the request */
|
||||
#define ACT_REPLACE 1 /* replace the matching header */
|
||||
|
@ -622,6 +631,8 @@ struct proxy {
|
|||
int srvtimeout; /* server I/O timeout (in milliseconds) */
|
||||
int contimeout; /* connect timeout (in milliseconds) */
|
||||
char *id; /* proxy id */
|
||||
struct list pendconns; /* pending connections with no server assigned yet */
|
||||
int nbpend; /* number of pending connections with no server assigned yet */
|
||||
int nbconn; /* # of active sessions */
|
||||
unsigned int cum_conn; /* cumulated number of processed sessions */
|
||||
int maxconn; /* max # of active sessions */
|
||||
|
@ -1781,57 +1792,94 @@ struct task *task_queue(struct task *task) {
|
|||
/*********************************************************************/
|
||||
|
||||
/*
|
||||
* returns the first pending connection of server <s> or NULL if none.
|
||||
* Detaches pending connection <p>, decreases the pending count, and frees
|
||||
* the pending connection. The connection might have been queued to a specific
|
||||
* server as well as to the proxy. The session also gets marked unqueued.
|
||||
*/
|
||||
static inline struct pendconn *pendconn_peek(struct server *s) {
|
||||
static void pendconn_free(struct pendconn *p) {
|
||||
LIST_DEL(&p->list);
|
||||
p->sess->pend_pos = NULL;
|
||||
if (p->srv)
|
||||
p->srv->nbpend--;
|
||||
else
|
||||
p->sess->proxy->nbpend--;
|
||||
pool_free(pendconn, p);
|
||||
}
|
||||
|
||||
/* Returns the first pending connection for server <s>, which may be NULL if
|
||||
* nothing is pending.
|
||||
*/
|
||||
static inline struct pendconn *pendconn_from_srv(struct server *s) {
|
||||
if (!s->nbpend)
|
||||
return NULL;
|
||||
|
||||
return LIST_ELEM(s->pendconns.n, struct pendconn *, list);
|
||||
}
|
||||
|
||||
/*
|
||||
* Detaches pending connection <p>, decreases the pending count, and frees
|
||||
* the pending connection.
|
||||
/* Returns the first pending connection for proxy <px>, which may be NULL if
|
||||
* nothing is pending.
|
||||
*/
|
||||
static inline void pendconn_free(struct pendconn *p) {
|
||||
LIST_DEL(&p->list);
|
||||
p->sess->pend_pos = NULL;
|
||||
p->srv->nbpend--;
|
||||
pool_free(pendconn, p);
|
||||
static inline struct pendconn *pendconn_from_px(struct proxy *px) {
|
||||
if (!px->nbpend)
|
||||
return NULL;
|
||||
|
||||
return LIST_ELEM(px->pendconns.n, struct pendconn *, list);
|
||||
}
|
||||
|
||||
/* detaches the first pending connection for server <s> and returns its
|
||||
* associated session. If no pending connection is found, NULL is returned.
|
||||
/* Detaches the next pending connection for either current session's server or
|
||||
* current session's proxy, and returns its associated session. If no pending
|
||||
* connection is found, NULL is returned. Note that cur->srv cannot be NULL.
|
||||
*/
|
||||
static inline struct session *pendconn_get(struct server *s) {
|
||||
static struct session *pendconn_get_next_sess(struct session *cur) {
|
||||
struct pendconn *p;
|
||||
struct session *sess;
|
||||
|
||||
p = pendconn_peek(s);
|
||||
if (!p)
|
||||
return NULL;
|
||||
p = pendconn_from_srv(cur->srv);
|
||||
if (!p) {
|
||||
p = pendconn_from_px(cur->proxy);
|
||||
if (!p)
|
||||
return NULL;
|
||||
p->sess->srv = cur->srv;
|
||||
}
|
||||
sess = p->sess;
|
||||
pendconn_free(p);
|
||||
return sess;
|
||||
}
|
||||
|
||||
/* adds the session <sess> to the pending connection list of server <srv>.
|
||||
* All counters and back pointers are updated accordingly. Returns NULL if
|
||||
* no memory is available, otherwise the pendconn itself.
|
||||
/* Checks if other sessions are waiting for the same server, and wakes the
|
||||
* first one up. Note that cur->srv cannot be NULL.
|
||||
*/
|
||||
static struct pendconn *pendconn_add(struct server *srv, struct session *sess) {
|
||||
void offer_connection_slot(struct session *cur) {
|
||||
struct session *sess;
|
||||
|
||||
sess = pendconn_get_next_sess(cur);
|
||||
if (sess == NULL)
|
||||
return;
|
||||
task_wakeup(&rq, sess->task);
|
||||
}
|
||||
|
||||
/* Adds the session <sess> to the pending connection list of server <sess>->srv
|
||||
* or to the one of <sess>->proxy if srv is NULL. All counters and back pointers
|
||||
* are updated accordingly. Returns NULL if no memory is available, otherwise the
|
||||
* pendconn itself.
|
||||
*/
|
||||
static struct pendconn *pendconn_add(struct session *sess) {
|
||||
struct pendconn *p;
|
||||
|
||||
p = pool_alloc(pendconn);
|
||||
if (!p)
|
||||
return NULL;
|
||||
|
||||
LIST_ADDQ(&srv->pendconns, &p->list);
|
||||
p->sess = sess;
|
||||
p->srv = srv;
|
||||
sess->pend_pos = p;
|
||||
srv->nbpend++;
|
||||
p->sess = sess;
|
||||
p->srv = sess->srv;
|
||||
if (sess->srv) {
|
||||
LIST_ADDQ(&sess->srv->pendconns, &p->list);
|
||||
sess->srv->nbpend++;
|
||||
} else {
|
||||
LIST_ADDQ(&sess->proxy->pendconns, &p->list);
|
||||
sess->proxy->nbpend++;
|
||||
}
|
||||
return p;
|
||||
}
|
||||
|
||||
|
@ -1860,7 +1908,7 @@ static int get_original_dst(int fd, struct sockaddr_in *sa, socklen_t *salen) {
|
|||
/*
|
||||
* frees the context associated to a session. It must have been removed first.
|
||||
*/
|
||||
static inline void session_free(struct session *s) {
|
||||
static void session_free(struct session *s) {
|
||||
if (s->pend_pos)
|
||||
pendconn_free(s->pend_pos);
|
||||
if (s->req)
|
||||
|
@ -2053,60 +2101,99 @@ static inline struct server *get_server_sh(struct proxy *px, char *addr, int len
|
|||
|
||||
|
||||
/*
|
||||
* This function initiates a connection to the current server (s->srv) if (s->direct)
|
||||
* is set, or to the dispatch server if (s->direct) is 0.
|
||||
* It can return one of :
|
||||
* - SN_ERR_NONE if everything's OK
|
||||
* - SN_ERR_SRVTO if there are no more servers
|
||||
* - SN_ERR_SRVCL if the connection was refused by the server
|
||||
* - SN_ERR_PRXCOND if the connection has been limited by the proxy (maxconn)
|
||||
* - SN_ERR_RESOURCE if a system resource is lacking (eg: fd limits, ports, ...)
|
||||
* - SN_ERR_INTERNAL for any other purely internal errors
|
||||
* Additionnally, in the case of SN_ERR_RESOURCE, an emergency log will be emitted.
|
||||
* This function marks the session as 'assigned' in direct or dispatch modes,
|
||||
* or tries to assign one in balance mode, according to the algorithm. It does
|
||||
* nothing if the session had already been assigned a server.
|
||||
*
|
||||
* It may return :
|
||||
* SRV_STATUS_OK if everything is OK.
|
||||
* SRV_STATUS_NOSRV if no server is available
|
||||
* SRV_STATUS_FULL if all servers are saturated
|
||||
* SRV_STATUS_INTERNAL for other unrecoverable errors.
|
||||
*
|
||||
* Upon successful return, the session flag SN_ASSIGNED to indicate that it does
|
||||
* not need to be called anymore. This usually means that s->srv can be trusted
|
||||
* in balance and direct modes. This flag is not cleared, so it's to the caller
|
||||
* to clear it if required (eg: redispatch).
|
||||
*
|
||||
*/
|
||||
int connect_server(struct session *s) {
|
||||
int fd;
|
||||
|
||||
int assign_server(struct session *s) {
|
||||
#ifdef DEBUG_FULL
|
||||
fprintf(stderr,"connect_server : s=%p\n",s);
|
||||
fprintf(stderr,"assign_server : s=%p\n",s);
|
||||
#endif
|
||||
|
||||
if (s->flags & SN_DIRECT) { /* srv cannot be null */
|
||||
s->srv_addr = s->srv->addr;
|
||||
if (s->pend_pos)
|
||||
return SRV_STATUS_INTERNAL;
|
||||
|
||||
if (!(s->flags & SN_ASSIGNED)) {
|
||||
if ((s->proxy->options & PR_O_BALANCE) && !(s->flags & SN_DIRECT)) {
|
||||
if (!s->proxy->srv_act && !s->proxy->srv_bck)
|
||||
return SRV_STATUS_NOSRV;
|
||||
|
||||
if (s->proxy->options & PR_O_BALANCE_RR) {
|
||||
s->srv = get_server_rr_with_conns(s->proxy);
|
||||
if (!s->srv)
|
||||
return SRV_STATUS_FULL;
|
||||
}
|
||||
else if (s->proxy->options & PR_O_BALANCE_SH) {
|
||||
int len;
|
||||
|
||||
if (s->cli_addr.ss_family == AF_INET)
|
||||
len = 4;
|
||||
else if (s->cli_addr.ss_family == AF_INET6)
|
||||
len = 16;
|
||||
else /* unknown IP family */
|
||||
return SRV_STATUS_INTERNAL;
|
||||
|
||||
s->srv = get_server_sh(s->proxy,
|
||||
(void *)&((struct sockaddr_in *)&s->cli_addr)->sin_addr,
|
||||
len);
|
||||
}
|
||||
else /* unknown balancing algorithm */
|
||||
return SRV_STATUS_INTERNAL;
|
||||
}
|
||||
s->flags |= SN_ASSIGNED;
|
||||
}
|
||||
else if (s->proxy->options & PR_O_BALANCE) {
|
||||
/* Ensure that srv will not be NULL */
|
||||
if (!s->proxy->srv_act && !s->proxy->srv_bck)
|
||||
return SN_ERR_SRVTO;
|
||||
return SRV_STATUS_OK;
|
||||
}
|
||||
|
||||
if (s->proxy->options & PR_O_BALANCE_RR) {
|
||||
struct server *srv;
|
||||
/*
|
||||
* This function assigns a server address to a session, and sets SN_ADDR_SET.
|
||||
* The address is taken from the currently assigned server, or from the
|
||||
* dispatch or transparent address.
|
||||
*
|
||||
* It may return :
|
||||
* SRV_STATUS_OK if everything is OK.
|
||||
* SRV_STATUS_INTERNAL for other unrecoverable errors.
|
||||
*
|
||||
* Upon successful return, the session flag SN_ADDR_SET is set. This flag is
|
||||
* not cleared, so it's to the caller to clear it if required.
|
||||
*
|
||||
*/
|
||||
int assign_server_address(struct session *s) {
|
||||
#ifdef DEBUG_FULL
|
||||
fprintf(stderr,"assign_server_address : s=%p\n",s);
|
||||
#endif
|
||||
|
||||
srv = get_server_rr_with_conns(s->proxy);
|
||||
if (!srv)
|
||||
srv = get_server_rr(s->proxy);
|
||||
s->srv_addr = srv->addr;
|
||||
s->srv = srv;
|
||||
if (s->flags & SN_DIRECT || s->proxy->options & PR_O_BALANCE) {
|
||||
/* A server is necessarily known for this session */
|
||||
if (!(s->flags & SN_ASSIGNED))
|
||||
return SRV_STATUS_INTERNAL;
|
||||
|
||||
s->srv_addr = s->srv->addr;
|
||||
|
||||
/* if this server remaps proxied ports, we'll use
|
||||
* the port the client connected to with an offset. */
|
||||
if (s->srv->state & SRV_MAPPORTS) {
|
||||
struct sockaddr_in sockname;
|
||||
socklen_t namelen = sizeof(sockname);
|
||||
|
||||
if (!(s->proxy->options & PR_O_TRANSP) ||
|
||||
get_original_dst(s->cli_fd, (struct sockaddr_in *)&sockname, &namelen) == -1)
|
||||
getsockname(s->cli_fd, (struct sockaddr *)&sockname, &namelen);
|
||||
s->srv_addr.sin_port = htons(ntohs(s->srv_addr.sin_port) + ntohs(sockname.sin_port));
|
||||
}
|
||||
else if (s->proxy->options & PR_O_BALANCE_SH) {
|
||||
struct server *srv;
|
||||
int len;
|
||||
|
||||
if (s->cli_addr.ss_family == AF_INET)
|
||||
len = 4;
|
||||
else if (s->cli_addr.ss_family == AF_INET6)
|
||||
len = 16;
|
||||
else /* unknown IP family */
|
||||
return SN_ERR_INTERNAL;
|
||||
|
||||
srv = get_server_sh(s->proxy,
|
||||
(void *)&((struct sockaddr_in *)&s->cli_addr)->sin_addr,
|
||||
len);
|
||||
s->srv_addr = srv->addr;
|
||||
s->srv = srv;
|
||||
}
|
||||
else /* unknown balancing algorithm */
|
||||
return SN_ERR_INTERNAL;
|
||||
}
|
||||
else if (*(int *)&s->proxy->dispatch_addr.sin_addr) {
|
||||
/* connect to the defined dispatch addr */
|
||||
|
@ -2118,20 +2205,101 @@ int connect_server(struct session *s) {
|
|||
|
||||
if (get_original_dst(s->cli_fd, &s->srv_addr, &salen) == -1) {
|
||||
qfprintf(stderr, "Cannot get original server address.\n");
|
||||
return SN_ERR_INTERNAL;
|
||||
return SRV_STATUS_INTERNAL;
|
||||
}
|
||||
}
|
||||
|
||||
/* if this server remaps proxied ports, we'll use
|
||||
* the port the client connected to with an offset. */
|
||||
if (s->srv != NULL && s->srv->state & SRV_MAPPORTS) {
|
||||
struct sockaddr_in sockname;
|
||||
socklen_t namelen = sizeof(sockname);
|
||||
s->flags |= SN_ADDR_SET;
|
||||
return SRV_STATUS_OK;
|
||||
}
|
||||
|
||||
if (!(s->proxy->options & PR_O_TRANSP) ||
|
||||
get_original_dst(s->cli_fd, (struct sockaddr_in *)&sockname, &namelen) == -1)
|
||||
getsockname(s->cli_fd, (struct sockaddr *)&sockname, &namelen);
|
||||
s->srv_addr.sin_port = htons(ntohs(s->srv_addr.sin_port) + ntohs(sockname.sin_port));
|
||||
/* This function assigns a server to session <s> if required, and can add the
|
||||
* connection to either the assigned server's queue or to the proxy's queue.
|
||||
*
|
||||
* Returns :
|
||||
*
|
||||
* SRV_STATUS_OK if everything is OK.
|
||||
* SRV_STATUS_NOSRV if no server is available
|
||||
* SRV_STATUS_QUEUED if the connection has been queued.
|
||||
* SRV_STATUS_FULL if the server(s) is/are saturated and the
|
||||
* connection could not be queued.
|
||||
* SRV_STATUS_INTERNAL for other unrecoverable errors.
|
||||
*
|
||||
*/
|
||||
int assign_server_and_queue(struct session *s) {
|
||||
struct pendconn *p;
|
||||
int err;
|
||||
|
||||
if (s->pend_pos)
|
||||
return SRV_STATUS_INTERNAL;
|
||||
|
||||
if (s->flags & SN_ASSIGNED) {
|
||||
/* a server does not need to be assigned, perhaps because we're in
|
||||
* direct mode, or in dispatch or transparent modes where the server
|
||||
* is not needed.
|
||||
*/
|
||||
if (s->srv &&
|
||||
s->srv->maxconn && s->srv->cur_sess >= s->srv->maxconn) {
|
||||
p = pendconn_add(s);
|
||||
if (p)
|
||||
return SRV_STATUS_QUEUED;
|
||||
else
|
||||
return SRV_STATUS_FULL;
|
||||
}
|
||||
return SRV_STATUS_OK;
|
||||
}
|
||||
|
||||
/* a server needs to be assigned */
|
||||
err = assign_server(s);
|
||||
switch (err) {
|
||||
case SRV_STATUS_OK:
|
||||
/* in balance mode, we might have servers with connection limits */
|
||||
if (s->srv != NULL &&
|
||||
s->srv->maxconn && s->srv->cur_sess >= s->srv->maxconn) {
|
||||
p = pendconn_add(s);
|
||||
if (p)
|
||||
return SRV_STATUS_QUEUED;
|
||||
else
|
||||
return SRV_STATUS_FULL;
|
||||
}
|
||||
return SRV_STATUS_OK;
|
||||
|
||||
case SRV_STATUS_FULL:
|
||||
/* queue this session into the proxy's queue */
|
||||
p = pendconn_add(s);
|
||||
if (p)
|
||||
return SRV_STATUS_QUEUED;
|
||||
else
|
||||
return SRV_STATUS_FULL;
|
||||
|
||||
case SRV_STATUS_NOSRV:
|
||||
case SRV_STATUS_INTERNAL:
|
||||
return err;
|
||||
default:
|
||||
return SRV_STATUS_INTERNAL;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* This function initiates a connection to the server assigned to this session
|
||||
* (s->srv, s->srv_addr). It will assign a server if none is assigned yet.
|
||||
* It can return one of :
|
||||
* - SN_ERR_NONE if everything's OK
|
||||
* - SN_ERR_SRVTO if there are no more servers
|
||||
* - SN_ERR_SRVCL if the connection was refused by the server
|
||||
* - SN_ERR_PRXCOND if the connection has been limited by the proxy (maxconn)
|
||||
* - SN_ERR_RESOURCE if a system resource is lacking (eg: fd limits, ports, ...)
|
||||
* - SN_ERR_INTERNAL for any other purely internal errors
|
||||
* Additionnally, in the case of SN_ERR_RESOURCE, an emergency log will be emitted.
|
||||
*/
|
||||
int connect_server(struct session *s) {
|
||||
int fd, err;
|
||||
|
||||
if (!(s->flags & SN_ADDR_SET)) {
|
||||
err = assign_server_address(s);
|
||||
if (err != SRV_STATUS_OK)
|
||||
return SN_ERR_INTERNAL;
|
||||
}
|
||||
|
||||
if ((fd = s->srv_fd = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP)) == -1) {
|
||||
|
@ -3624,7 +3792,7 @@ int process_cli(struct session *t) {
|
|||
if (srv->state & SRV_RUNNING || t->proxy->options & PR_O_PERSIST) {
|
||||
/* we found the server and it's usable */
|
||||
t->flags &= ~SN_CK_MASK;
|
||||
t->flags |= SN_CK_VALID | SN_DIRECT;
|
||||
t->flags |= SN_CK_VALID | SN_DIRECT | SN_ASSIGNED;
|
||||
t->srv = srv;
|
||||
break;
|
||||
} else {
|
||||
|
@ -3879,7 +4047,7 @@ int process_cli(struct session *t) {
|
|||
if (srv->state & SRV_RUNNING || t->proxy->options & PR_O_PERSIST) {
|
||||
/* we found the server and it's usable */
|
||||
t->flags &= ~SN_CK_MASK;
|
||||
t->flags |= SN_CK_VALID | SN_DIRECT;
|
||||
t->flags |= SN_CK_VALID | SN_DIRECT | SN_ASSIGNED;
|
||||
t->srv = srv;
|
||||
break;
|
||||
} else {
|
||||
|
@ -3976,7 +4144,7 @@ int process_cli(struct session *t) {
|
|||
if (srv->state & SRV_RUNNING || t->proxy->options & PR_O_PERSIST) {
|
||||
/* we found the server and it's usable */
|
||||
t->flags &= ~SN_CK_MASK;
|
||||
t->flags |= SN_CK_VALID | SN_DIRECT;
|
||||
t->flags |= SN_CK_VALID | SN_DIRECT | SN_ASSIGNED;
|
||||
t->srv = srv;
|
||||
break;
|
||||
} else {
|
||||
|
@ -4312,6 +4480,150 @@ int process_cli(struct session *t) {
|
|||
return 0;
|
||||
}
|
||||
|
||||
/* This function turns the server state into the SV_STCLOSE, and sets
|
||||
* indicators accordingly. Note that if <status> is 0, no message is
|
||||
* returned.
|
||||
*/
|
||||
void srv_close_with_err(struct session *t, int err, int finst, int status, int msglen, char *msg) {
|
||||
t->srv_state = SV_STCLOSE;
|
||||
if (status > 0) {
|
||||
t->logs.status = status;
|
||||
if (t->proxy->mode == PR_MODE_HTTP)
|
||||
client_return(t, msglen, msg);
|
||||
}
|
||||
if (!(t->flags & SN_ERR_MASK))
|
||||
t->flags |= err;
|
||||
if (!(t->flags & SN_FINST_MASK))
|
||||
t->flags |= finst;
|
||||
}
|
||||
|
||||
/*
|
||||
* This function checks the retry count during the connect() job.
|
||||
* It updates the session's srv_state and retries, so that the caller knows
|
||||
* what it has to do. It uses the last connection error to set the log when
|
||||
* it expires. It returns 1 when it has expired, and 0 otherwise.
|
||||
*/
|
||||
int srv_count_retry_down(struct session *t, int conn_err) {
|
||||
/* we are in front of a retryable error */
|
||||
t->conn_retries--;
|
||||
if (t->conn_retries < 0) {
|
||||
/* if not retryable anymore, let's abort */
|
||||
tv_eternity(&t->cnexpire);
|
||||
srv_close_with_err(t, conn_err, SN_FINST_C,
|
||||
503, t->proxy->errmsg.len503, t->proxy->errmsg.msg503);
|
||||
|
||||
/* We used to have a free connection slot. Since we'll never use it,
|
||||
* we have to pass it on to another session.
|
||||
*/
|
||||
if (t->srv)
|
||||
offer_connection_slot(t);
|
||||
return 1;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*
|
||||
* This function performs the retryable part of the connect() job.
|
||||
* It updates the session's srv_state and retries, so that the caller knows
|
||||
* what it has to do. It returns 1 when it breaks out of the loop, or 0 if
|
||||
* it needs to redispatch.
|
||||
*/
|
||||
int srv_retryable_connect(struct session *t) {
|
||||
int conn_err;
|
||||
|
||||
/* This loop ensures that we stop before the last retry in case of a
|
||||
* redispatchable server.
|
||||
*/
|
||||
do {
|
||||
/* initiate a connection to the server */
|
||||
conn_err = connect_server(t);
|
||||
switch (conn_err) {
|
||||
|
||||
case SN_ERR_NONE:
|
||||
//fprintf(stderr,"0: c=%d, s=%d\n", c, s);
|
||||
t->srv_state = SV_STCONN;
|
||||
return 1;
|
||||
|
||||
case SN_ERR_INTERNAL:
|
||||
tv_eternity(&t->cnexpire);
|
||||
srv_close_with_err(t, SN_ERR_INTERNAL, SN_FINST_C,
|
||||
500, t->proxy->errmsg.len500, t->proxy->errmsg.msg500);
|
||||
/* release other sessions waiting for this server */
|
||||
if (t->srv)
|
||||
offer_connection_slot(t);
|
||||
return 1;
|
||||
}
|
||||
/* ensure that we have enough retries left */
|
||||
if (srv_count_retry_down(t, conn_err))
|
||||
return 1;
|
||||
} while (t->srv == NULL || t->conn_retries > 0 || !(t->proxy->options & PR_O_REDISP));
|
||||
|
||||
/* We're on our last chance, and the REDISP option was specified.
|
||||
* We will ignore cookie and force to balance or use the dispatcher.
|
||||
*/
|
||||
t->flags &= ~(SN_DIRECT | SN_ASSIGNED | SN_ADDR_SET);
|
||||
t->srv = NULL; /* it's left to the dispatcher to choose a server */
|
||||
if ((t->flags & SN_CK_MASK) == SN_CK_VALID) {
|
||||
t->flags &= ~SN_CK_MASK;
|
||||
t->flags |= SN_CK_DOWN;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* This function performs the "redispatch" part of a connection attempt. It
|
||||
* will assign a server if required, queue the connection if required, and
|
||||
* handle errors that might arise at this level. It can change the server
|
||||
* state. It will return 1 if it encounters an error, switches the server
|
||||
* state, or has to queue a connection. Otherwise, it will return 0 indicating
|
||||
* that the connection is ready to use.
|
||||
*/
|
||||
|
||||
int srv_redispatch_connect(struct session *t) {
|
||||
int conn_err;
|
||||
|
||||
/* We know that we don't have any connection pending, so we will
|
||||
* try to get a new one, and wait in this state if it's queued
|
||||
*/
|
||||
conn_err = assign_server_and_queue(t);
|
||||
switch (conn_err) {
|
||||
case SRV_STATUS_OK:
|
||||
break;
|
||||
|
||||
case SRV_STATUS_NOSRV:
|
||||
tv_eternity(&t->cnexpire);
|
||||
srv_close_with_err(t, SN_ERR_SRVTO, SN_FINST_C,
|
||||
503, t->proxy->errmsg.len503, t->proxy->errmsg.msg503);
|
||||
|
||||
/* FIXME-20060501: we should not need this once we flush every session
|
||||
* when the last server goes down.
|
||||
*/
|
||||
/* release other sessions waiting for this server */
|
||||
if (t->srv)
|
||||
offer_connection_slot(t);
|
||||
return 1;
|
||||
|
||||
case SRV_STATUS_QUEUED:
|
||||
t->srv_state = SV_STIDLE;
|
||||
/* do nothing else and do not wake any other session up */
|
||||
return 1;
|
||||
|
||||
case SRV_STATUS_FULL:
|
||||
case SRV_STATUS_INTERNAL:
|
||||
default:
|
||||
tv_eternity(&t->cnexpire);
|
||||
srv_close_with_err(t, SN_ERR_INTERNAL, SN_FINST_C,
|
||||
500, t->proxy->errmsg.len500, t->proxy->errmsg.msg500);
|
||||
/* release other sessions waiting for this server */
|
||||
if (t->srv)
|
||||
offer_connection_slot(t);
|
||||
return 1;
|
||||
}
|
||||
/* if we get here, it's because we got SRV_STATUS_OK, which also
|
||||
* means that the connection has not been queued.
|
||||
*/
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* manages the server FSM and its socket. It returns 1 if a state has changed
|
||||
|
@ -4340,51 +4652,37 @@ int process_srv(struct session *t) {
|
|||
c == CL_STSHUTW ||
|
||||
(c == CL_STSHUTR && t->req->l == 0)) { /* give up */
|
||||
tv_eternity(&t->cnexpire);
|
||||
t->srv_state = SV_STCLOSE;
|
||||
if (!(t->flags & SN_ERR_MASK))
|
||||
t->flags |= SN_ERR_CLICL;
|
||||
if (!(t->flags & SN_FINST_MASK))
|
||||
t->flags |= SN_FINST_C;
|
||||
srv_close_with_err(t, SN_ERR_CLICL, SN_FINST_C, 0, 0, NULL);
|
||||
|
||||
/* it might be possible that we have been granted an access to the
|
||||
* server while waiting for a free slot. Since we'll never use it,
|
||||
* we have to pass it on to another session.
|
||||
*/
|
||||
if (t->srv)
|
||||
offer_connection_slot(t);
|
||||
return 1;
|
||||
}
|
||||
else { /* go to SV_STCONN */
|
||||
/* initiate a connection to the server */
|
||||
conn_err = connect_server(t);
|
||||
if (conn_err == SN_ERR_NONE) {
|
||||
//fprintf(stderr,"0: c=%d, s=%d\n", c, s);
|
||||
t->srv_state = SV_STCONN;
|
||||
}
|
||||
else { /* try again */
|
||||
while (t->conn_retries-- > 0) {
|
||||
if ((t->proxy->options & PR_O_REDISP) && (t->conn_retries == 0)) {
|
||||
t->flags &= ~SN_DIRECT; /* ignore cookie and force to use the dispatcher */
|
||||
t->srv = NULL; /* it's left to the dispatcher to choose a server */
|
||||
if ((t->flags & SN_CK_MASK) == SN_CK_VALID) {
|
||||
t->flags &= ~SN_CK_MASK;
|
||||
t->flags |= SN_CK_DOWN;
|
||||
}
|
||||
}
|
||||
else {
|
||||
/* Right now, we will need to create a connection to the server.
|
||||
* We might already have tried, and got a connection pending, in
|
||||
* which case we will not do anything till it's pending. It's up
|
||||
* to any other session to release it and wake us up again.
|
||||
*/
|
||||
if (t->pend_pos)
|
||||
return 0;
|
||||
|
||||
conn_err = connect_server(t);
|
||||
if (conn_err == SN_ERR_NONE) {
|
||||
t->srv_state = SV_STCONN;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (t->conn_retries < 0) {
|
||||
/* if conn_retries < 0 or other error, let's abort */
|
||||
tv_eternity(&t->cnexpire);
|
||||
t->srv_state = SV_STCLOSE;
|
||||
t->logs.status = 503;
|
||||
if (t->proxy->mode == PR_MODE_HTTP)
|
||||
client_return(t, t->proxy->errmsg.len503, t->proxy->errmsg.msg503);
|
||||
if (!(t->flags & SN_ERR_MASK))
|
||||
t->flags |= conn_err; /* report the precise connect() error */
|
||||
if (!(t->flags & SN_FINST_MASK))
|
||||
t->flags |= SN_FINST_C;
|
||||
}
|
||||
}
|
||||
return 1;
|
||||
do {
|
||||
/* first, get a connection */
|
||||
if (srv_redispatch_connect(t))
|
||||
return t->srv_state != SV_STIDLE;
|
||||
|
||||
/* try to (re-)connect to the server, and fail if we expire the
|
||||
* number of retries.
|
||||
*/
|
||||
if (srv_retryable_connect(t))
|
||||
return t->srv_state != SV_STIDLE;
|
||||
|
||||
} while (1);
|
||||
}
|
||||
}
|
||||
else if (s == SV_STCONN) { /* connection in progress */
|
||||
|
@ -4393,44 +4691,35 @@ int process_srv(struct session *t) {
|
|||
return 0; /* nothing changed */
|
||||
}
|
||||
else if (t->res_sw == RES_SILENT || t->res_sw == RES_ERROR) {
|
||||
/* timeout, asynchronous connect error or first write error */
|
||||
//fprintf(stderr,"2: c=%d, s=%d\n", c, s);
|
||||
/* timeout, connect error or first write error */
|
||||
//FD_CLR(t->srv_fd, StaticWriteEvent);
|
||||
|
||||
fd_delete(t->srv_fd);
|
||||
if (t->srv)
|
||||
t->srv->cur_sess--;
|
||||
//close(t->srv_fd);
|
||||
t->conn_retries--;
|
||||
if (t->conn_retries >= 0) {
|
||||
if ((t->proxy->options & PR_O_REDISP) && (t->conn_retries == 0)) {
|
||||
t->flags &= ~SN_DIRECT; /* ignore cookie and force to use the dispatcher */
|
||||
t->srv = NULL; /* it's left to the dispatcher to choose a server */
|
||||
if ((t->flags & SN_CK_MASK) == SN_CK_VALID) {
|
||||
t->flags &= ~SN_CK_MASK;
|
||||
t->flags |= SN_CK_DOWN;
|
||||
}
|
||||
}
|
||||
conn_err = connect_server(t);
|
||||
if (conn_err == SN_ERR_NONE)
|
||||
return 0; /* no state changed */
|
||||
}
|
||||
else if (t->res_sw == RES_SILENT)
|
||||
|
||||
if (t->res_sw == RES_SILENT)
|
||||
conn_err = SN_ERR_SRVTO; // it was a connect timeout.
|
||||
else
|
||||
conn_err = SN_ERR_SRVCL; // it was a connect error.
|
||||
conn_err = SN_ERR_SRVCL; // it was an asynchronous connect error.
|
||||
|
||||
/* if conn_retries < 0 or other error, let's abort */
|
||||
tv_eternity(&t->cnexpire);
|
||||
t->srv_state = SV_STCLOSE;
|
||||
t->logs.status = 503;
|
||||
if (t->proxy->mode == PR_MODE_HTTP)
|
||||
client_return(t, t->proxy->errmsg.len503, t->proxy->errmsg.msg503);
|
||||
if (!(t->flags & SN_ERR_MASK))
|
||||
t->flags |= conn_err;
|
||||
if (!(t->flags & SN_FINST_MASK))
|
||||
t->flags |= SN_FINST_C;
|
||||
/* TODO : check if there are pending connections on this server */
|
||||
return 1;
|
||||
/* ensure that we have enough retries left */
|
||||
if (srv_count_retry_down(t, conn_err))
|
||||
return 1;
|
||||
|
||||
do {
|
||||
/* Now we will try to either reconnect to the same server or
|
||||
* connect to another server. If the connection gets queued
|
||||
* because all servers are saturated, then we will go back to
|
||||
* the SV_STIDLE state.
|
||||
*/
|
||||
if (srv_retryable_connect(t))
|
||||
return t->srv_state != SV_STCONN;
|
||||
|
||||
/* we need to redispatch the connection to another server */
|
||||
if (srv_redispatch_connect(t))
|
||||
return t->srv_state != SV_STCONN;
|
||||
} while (1);
|
||||
}
|
||||
else { /* no error or write 0 */
|
||||
t->logs.t_connect = tv_diff(&t->logs.tv_accept, &now);
|
||||
|
@ -4521,7 +4810,12 @@ int process_srv(struct session *t) {
|
|||
Alert("Blocking cacheable cookie in response from instance %s, server %s.\n", t->proxy->id, t->srv->id);
|
||||
send_log(t->proxy, LOG_ALERT, "Blocking cacheable cookie in response from instance %s, server %s.\n", t->proxy->id, t->srv->id);
|
||||
|
||||
/* TODO : check if there are pending connections on this server */
|
||||
/* We used to have a free connection slot. Since we'll never use it,
|
||||
* we have to pass it on to another session.
|
||||
*/
|
||||
if (t->srv)
|
||||
offer_connection_slot(t);
|
||||
|
||||
return 1;
|
||||
}
|
||||
}
|
||||
|
@ -4540,7 +4834,12 @@ int process_srv(struct session *t) {
|
|||
t->flags |= SN_ERR_PRXCOND;
|
||||
if (!(t->flags & SN_FINST_MASK))
|
||||
t->flags |= SN_FINST_H;
|
||||
/* TODO : check if there are pending connections on this server */
|
||||
/* We used to have a free connection slot. Since we'll never use it,
|
||||
* we have to pass it on to another session.
|
||||
*/
|
||||
if (t->srv)
|
||||
offer_connection_slot(t);
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
|
@ -4974,7 +5273,12 @@ int process_srv(struct session *t) {
|
|||
t->flags |= SN_ERR_SRVCL;
|
||||
if (!(t->flags & SN_FINST_MASK))
|
||||
t->flags |= SN_FINST_H;
|
||||
/* TODO : check if there are pending connections on this server */
|
||||
/* We used to have a free connection slot. Since we'll never use it,
|
||||
* we have to pass it on to another session.
|
||||
*/
|
||||
if (t->srv)
|
||||
offer_connection_slot(t);
|
||||
|
||||
return 1;
|
||||
}
|
||||
/* end of client write or end of server read.
|
||||
|
@ -5004,7 +5308,12 @@ int process_srv(struct session *t) {
|
|||
t->flags |= SN_ERR_SRVTO;
|
||||
if (!(t->flags & SN_FINST_MASK))
|
||||
t->flags |= SN_FINST_H;
|
||||
/* TODO : check if there are pending connections on this server */
|
||||
/* We used to have a free connection slot. Since we'll never use it,
|
||||
* we have to pass it on to another session.
|
||||
*/
|
||||
if (t->srv)
|
||||
offer_connection_slot(t);
|
||||
|
||||
return 1;
|
||||
}
|
||||
/* last client read and buffer empty */
|
||||
|
@ -5099,7 +5408,12 @@ int process_srv(struct session *t) {
|
|||
t->flags |= SN_ERR_SRVCL;
|
||||
if (!(t->flags & SN_FINST_MASK))
|
||||
t->flags |= SN_FINST_D;
|
||||
/* TODO : check if there are pending connections on this server */
|
||||
/* We used to have a free connection slot. Since we'll never use it,
|
||||
* we have to pass it on to another session.
|
||||
*/
|
||||
if (t->srv)
|
||||
offer_connection_slot(t);
|
||||
|
||||
return 1;
|
||||
}
|
||||
/* last read, or end of client write */
|
||||
|
@ -5208,7 +5522,12 @@ int process_srv(struct session *t) {
|
|||
t->flags |= SN_ERR_SRVCL;
|
||||
if (!(t->flags & SN_FINST_MASK))
|
||||
t->flags |= SN_FINST_D;
|
||||
/* TODO : check if there are pending connections on this server */
|
||||
/* We used to have a free connection slot. Since we'll never use it,
|
||||
* we have to pass it on to another session.
|
||||
*/
|
||||
if (t->srv)
|
||||
offer_connection_slot(t);
|
||||
|
||||
return 1;
|
||||
}
|
||||
else if ((c == CL_STSHUTR || c == CL_STCLOSE) && (req->l == 0)) {
|
||||
|
@ -5219,7 +5538,12 @@ int process_srv(struct session *t) {
|
|||
t->srv->cur_sess--;
|
||||
//close(t->srv_fd);
|
||||
t->srv_state = SV_STCLOSE;
|
||||
/* TODO : check if there are pending connections on this server */
|
||||
/* We used to have a free connection slot. Since we'll never use it,
|
||||
* we have to pass it on to another session.
|
||||
*/
|
||||
if (t->srv)
|
||||
offer_connection_slot(t);
|
||||
|
||||
return 1;
|
||||
}
|
||||
else if (tv_cmp2_ms(&t->swexpire, &now) <= 0) {
|
||||
|
@ -5234,7 +5558,12 @@ int process_srv(struct session *t) {
|
|||
t->flags |= SN_ERR_SRVTO;
|
||||
if (!(t->flags & SN_FINST_MASK))
|
||||
t->flags |= SN_FINST_D;
|
||||
/* TODO : check if there are pending connections on this server */
|
||||
/* We used to have a free connection slot. Since we'll never use it,
|
||||
* we have to pass it on to another session.
|
||||
*/
|
||||
if (t->srv)
|
||||
offer_connection_slot(t);
|
||||
|
||||
return 1;
|
||||
}
|
||||
else if (req->l == 0) {
|
||||
|
@ -5271,7 +5600,12 @@ int process_srv(struct session *t) {
|
|||
t->flags |= SN_ERR_SRVCL;
|
||||
if (!(t->flags & SN_FINST_MASK))
|
||||
t->flags |= SN_FINST_D;
|
||||
/* TODO : check if there are pending connections on this server */
|
||||
/* We used to have a free connection slot. Since we'll never use it,
|
||||
* we have to pass it on to another session.
|
||||
*/
|
||||
if (t->srv)
|
||||
offer_connection_slot(t);
|
||||
|
||||
return 1;
|
||||
}
|
||||
else if (t->res_sr == RES_NULL || c == CL_STSHUTW || c == CL_STCLOSE) {
|
||||
|
@ -5282,7 +5616,12 @@ int process_srv(struct session *t) {
|
|||
t->srv->cur_sess--;
|
||||
//close(t->srv_fd);
|
||||
t->srv_state = SV_STCLOSE;
|
||||
/* TODO : check if there are pending connections on this server */
|
||||
/* We used to have a free connection slot. Since we'll never use it,
|
||||
* we have to pass it on to another session.
|
||||
*/
|
||||
if (t->srv)
|
||||
offer_connection_slot(t);
|
||||
|
||||
return 1;
|
||||
}
|
||||
else if (tv_cmp2_ms(&t->srexpire, &now) <= 0) {
|
||||
|
@ -5297,7 +5636,12 @@ int process_srv(struct session *t) {
|
|||
t->flags |= SN_ERR_SRVTO;
|
||||
if (!(t->flags & SN_FINST_MASK))
|
||||
t->flags |= SN_FINST_D;
|
||||
/* TODO : check if there are pending connections on this server */
|
||||
/* We used to have a free connection slot. Since we'll never use it,
|
||||
* we have to pass it on to another session.
|
||||
*/
|
||||
if (t->srv)
|
||||
offer_connection_slot(t);
|
||||
|
||||
return 1;
|
||||
}
|
||||
else if (rep->l == BUFSIZE) { /* no room to read more data */
|
||||
|
@ -6666,8 +7010,11 @@ int cfg_parse_listen(char *file, int linenum, char **args) {
|
|||
Alert("parsing [%s:%d] : out of memory.\n", file, linenum);
|
||||
return -1;
|
||||
}
|
||||
|
||||
curproxy->next = proxy;
|
||||
proxy = curproxy;
|
||||
LIST_INIT(&curproxy->pendconns);
|
||||
|
||||
curproxy->id = strdup(args[1]);
|
||||
|
||||
/* parse the listener address if any */
|
||||
|
|
Loading…
Reference in New Issue