diff --git a/haproxy.c b/haproxy.c index b8cc48d0e..81e793475 100644 --- a/haproxy.c +++ b/haproxy.c @@ -514,7 +514,7 @@ struct server { int cur_sess; /* number of currently active sessions (including syn_sent) */ unsigned int cum_sess; /* cumulated number of sessions really sent to this server */ unsigned char uweight, eweight; /* user-specified weight-1, and effective weight-1 */ - unsigned int wscore; /* weight score, used during srv map computation */ + unsigned short wsquare; /* eweight*eweight, to speed up map computation */ struct proxy *proxy; /* the proxy this server belongs to */ }; @@ -581,12 +581,8 @@ struct proxy { struct in_addr mon_net, mon_mask; /* don't forward connections from this net (network order) FIXME: should support IPv6 */ int state; /* proxy state */ struct sockaddr_in dispatch_addr; /* the default address to connect to */ - struct server *srv; /* known servers */ - int srv_act, srv_bck; /* # of running servers */ - int tot_wact, tot_wbck; /* total weights of active and backup servers */ - struct server **srv_map; /* the server map used to apply weights */ - int srv_map_sz; /* the size of the effective server map */ - int srv_rr_idx; /* next server to be elected in round robin mode */ + struct server *srv, *cursrv; /* known servers, current server */ + int srv_act, srv_bck; /* # of servers */ char *cookie_name; /* name of the cookie to look for */ int cookie_len; /* strlen(cookie_name), computed only once */ char *appsession_name; /* name of the cookie to look for */ @@ -1816,101 +1812,77 @@ static inline void session_free(struct session *s) { /* * This function recounts the number of usable active and backup servers for * proxy

. These numbers are returned into the p->srv_act and p->srv_bck. - * This function also recomputes the total active and backup weights. */ -static void recount_servers(struct proxy *px) { +static inline void recount_servers(struct proxy *px) { struct server *srv; - px->srv_act = 0; px->srv_bck = px->tot_wact = px->tot_wbck = 0; + px->srv_act = 0; px->srv_bck = 0; for (srv = px->srv; srv != NULL; srv = srv->next) { if (srv->state & SRV_RUNNING) { - if (srv->state & SRV_BACKUP) { + if (srv->state & SRV_BACKUP) px->srv_bck++; - px->tot_wbck += srv->eweight + 1; - } else { + else px->srv_act++; - px->tot_wact += srv->eweight + 1; - } } } } -/* This function recomputes the server map for proxy px. It - * relies on px->tot_wact and px->tot_wbck, so it must be - * called after recount_servers(). It also expects px->srv_map - * to be initialized to the largest value needed. - */ -static void recalc_server_map(struct proxy *px) { - int o, tot, flag; - struct server *cur, *best; - - if (px->srv_act) { - flag = SRV_RUNNING; - tot = px->tot_wact; - } else if (px->srv_bck) { - flag = SRV_RUNNING | SRV_BACKUP; - if (px->options & PR_O_USE_ALL_BK) - tot = px->tot_wbck; - else - tot = 1; /* the first server is enough */ - } else { - px->srv_map_sz = 0; - return; - } - - /* this algorithm gives priority to the first server, which means that - * it will respect the declaration order for equivalent weights, and - * that whatever the weights, the first server called will always be - * the first declard. This is an important asumption for the backup - * case, where we want the first server only. - */ - for (cur = px->srv; cur; cur = cur->next) - cur->wscore = 0; - - for (o = 0; o < tot; o++) { - int max = 0; - best = NULL; - for (cur = px->srv; cur; cur = cur->next) { - if ((cur->state & (SRV_RUNNING | SRV_BACKUP)) == flag) { - int v; - - /* If we are forced to return only one server, we don't want to - * go further, because we would return the wrong one due to - * divide overflow. - */ - if (tot == 1) { - best = cur; - break; - } - - cur->wscore += cur->eweight + 1; - v = (cur->wscore + tot) / tot; /* result between 0 and 3 */ - if (best == NULL || v > max) { - max = v; - best = cur; - } - } - } - px->srv_map[o] = best; - best->wscore -= tot; - } - px->srv_map_sz = tot; -} - /* * This function tries to find a running server for the proxy following * the round-robin method. Depending on the number of active/backup servers, * it will either look for active servers, or for backup servers. - * If any server is found, it will be returned and px->srv_rr_idx will be updated + * If any server is found, it will be returned and px->cursrv will be updated * to point to the next server. If no valid server is found, NULL is returned. */ static inline struct server *get_server_rr(struct proxy *px) { - if (px->srv_map_sz == 0) - return NULL; + struct server *srv; + struct server *end; - if (px->srv_rr_idx < 0 || px->srv_rr_idx >= px->srv_map_sz) - px->srv_rr_idx = 0; - return px->srv_map[px->srv_rr_idx++]; + if (px->srv_act) { + srv = px->cursrv; + if (srv == NULL) + srv = px->srv; + end = srv; + do { + if ((srv->state & (SRV_RUNNING | SRV_BACKUP)) == SRV_RUNNING) { + px->cursrv = srv->next; + return srv; + } + + srv = srv->next; + if (srv == NULL) + srv = px->srv; + } while (srv != end); + /* note that theorically we should not get there */ + } + + if (px->srv_bck) { + /* By default, we look for the first backup server if all others are + * DOWN. But in some cases, it may be desirable to load-balance across + * all backup servers. + */ + if (px->options & PR_O_USE_ALL_BK) + srv = px->cursrv; + else + srv = px->srv; + + if (srv == NULL) + srv = px->srv; + end = srv; + do { + if (srv->state & SRV_RUNNING) { + px->cursrv = srv->next; + return srv; + } + srv = srv->next; + if (srv == NULL) + srv = px->srv; + } while (srv != end); + /* note that theorically we should not get there */ + } + + /* if we get there, it means there are no available servers at all */ + return NULL; } @@ -1922,20 +1894,58 @@ static inline struct server *get_server_rr(struct proxy *px) { * NULL is returned. */ static inline struct server *get_server_sh(struct proxy *px, char *addr, int len) { - unsigned int h, l; + struct server *srv; - if (px->srv_map_sz == 0) - return NULL; + if (px->srv_act) { + unsigned int h, l; - l = h = 0; - if (px->srv_act > 1) { - while ((l + sizeof (int)) <= len) { - h ^= ntohl(*(unsigned int *)(&addr[l])); - l += sizeof (int); + l = h = 0; + if (px->srv_act > 1) { + while ((l + sizeof (int)) <= len) { + h ^= ntohl(*(unsigned int *)(&addr[l])); + l += sizeof (int); + } + h %= px->srv_act; + } + + for (srv = px->srv; srv; srv = srv->next) { + if ((srv->state & (SRV_RUNNING | SRV_BACKUP)) == SRV_RUNNING) { + if (!h) + return srv; + h--; + } } - h %= px->srv_map_sz; + /* note that theorically we should not get there */ } - return px->srv_map[h]; + + if (px->srv_bck) { + unsigned int h, l; + + /* By default, we look for the first backup server if all others are + * DOWN. But in some cases, it may be desirable to load-balance across + * all backup servers. + */ + l = h = 0; + if (px->srv_bck > 1 && px->options & PR_O_USE_ALL_BK) { + while ((l + sizeof (int)) <= len) { + h ^= ntohl(*(unsigned int *)(&addr[l])); + l += sizeof (int); + } + h %= px->srv_bck; + } + + for (srv = px->srv; srv; srv = srv->next) { + if (srv->state & SRV_RUNNING) { + if (!h) + return srv; + h--; + } + } + /* note that theorically we should not get there */ + } + + /* if we get there, it means there are no available servers at all */ + return NULL; } @@ -5332,7 +5342,6 @@ int process_chk(struct task *t) { s->state &= ~SRV_RUNNING; if (s->health == s->rise) { recount_servers(s->proxy); - recalc_server_map(s->proxy); Warning("%sServer %s/%s DOWN. %d active and %d backup servers left.%s\n", s->state & SRV_BACKUP ? "Backup " : "", s->proxy->id, s->id, s->proxy->srv_act, s->proxy->srv_bck, @@ -5368,7 +5377,6 @@ int process_chk(struct task *t) { if (s->health == s->rise) { recount_servers(s->proxy); - recalc_server_map(s->proxy); Warning("%sServer %s/%s UP. %d active and %d backup servers online.%s\n", s->state & SRV_BACKUP ? "Backup " : "", s->proxy->id, s->id, s->proxy->srv_act, s->proxy->srv_bck, @@ -5399,7 +5407,6 @@ int process_chk(struct task *t) { if (s->health == s->rise) { recount_servers(s->proxy); - recalc_server_map(s->proxy); Warning("%sServer %s/%s DOWN. %d active and %d backup servers left.%s\n", s->state & SRV_BACKUP ? "Backup " : "", s->proxy->id, s->id, s->proxy->srv_act, s->proxy->srv_bck, @@ -7016,9 +7023,13 @@ int cfg_parse_listen(char *file, int linenum, char **args) { return -1; } - /* the servers are linked backwards first */ - newsrv->next = curproxy->srv; - curproxy->srv = newsrv; + if (curproxy->srv == NULL) + curproxy->srv = newsrv; + else + curproxy->cursrv->next = newsrv; + curproxy->cursrv = newsrv; + + newsrv->next = NULL; newsrv->proxy = curproxy; do_check = 0; @@ -7832,6 +7843,7 @@ int readcfgfile(char *file) { } while (curproxy != NULL) { + curproxy->cursrv = NULL; if (curproxy->state == PR_STSTOPPED) { curproxy = curproxy->next; continue; @@ -7888,59 +7900,29 @@ int readcfgfile(char *file) { file, curproxy->id); cfgerr++; } - } + else { + struct server *srv; + int pgcd; - /* first, we will invert the servers list order */ - newsrv = NULL; - while (curproxy->srv) { - struct server *next; + if (newsrv) { + /* We will factor the weights to reduce the table, + * using Euclide's largest common divisor algorithm + */ + pgcd = newsrv->uweight + 1; + for (srv = newsrv->next; srv && pgcd > 1; srv = srv->next) { + int t, w; - next = curproxy->srv->next; - curproxy->srv->next = newsrv; - newsrv = curproxy->srv; - if (!next) - break; - curproxy->srv = next; - } - - /* now, newsrv == curproxy->srv */ - if (newsrv) { - struct server *srv; - int pgcd; - int act, bck; - - /* We will factor the weights to reduce the table, - * using Euclide's largest common divisor algorithm - */ - pgcd = newsrv->uweight + 1; - for (srv = newsrv->next; srv && pgcd > 1; srv = srv->next) { - int t, w; - - w = srv->uweight + 1; - while (w) { - t = pgcd % w; - pgcd = w; - w = t; + w = srv->uweight + 1; + while (w) { + t = pgcd % w; + pgcd = w; + w = t; } + } + for (srv = newsrv; srv; srv = srv->next) + srv->eweight = ((srv->uweight + 1) / pgcd) - 1; + } } - - act = bck = 0; - for (srv = newsrv; srv; srv = srv->next) { - srv->eweight = ((srv->uweight + 1) / pgcd) - 1; - if (srv->state & SRV_BACKUP) - bck += srv->eweight + 1; - else - act += srv->eweight + 1; - } - - /* this is the largest map we will ever need for this servers list */ - if (act < bck) - act = bck; - - curproxy->srv_map = (struct server **)calloc(act, sizeof(struct server *)); - /* recounts servers and their weights */ - recount_servers(curproxy); - recalc_server_map(curproxy); } if (curproxy->options & PR_O_LOGASAP)