diff --git a/CHANGELOG b/CHANGELOG index 67e9c5654..af549111d 100644 --- a/CHANGELOG +++ b/CHANGELOG @@ -1,6 +1,14 @@ ChangeLog : =========== +2006/04/15 : 1.2.12 + Very few changes preparing for more important changes to support per-server + session limitations and queueing : + - ignore leading empty lines in HTTP requests as suggested by RFC2616. + - added the 'weight' parameter to the servers, limited to 1..256. It applies + to roundrobin and source hash. + - the optional '-s' option could clobber '-st' and '-sf' if compiled in. + 2006/03/30 : 1.2.11.1 - under some conditions, it might have been possible that when the last dead server became available, it would not have been used diff --git a/Makefile b/Makefile index 765c1e76f..71ceeeefd 100644 --- a/Makefile +++ b/Makefile @@ -2,7 +2,7 @@ # You should use it this way : # make TARGET=os CPU=cpu -VERSION := 1.2.11.1 +VERSION := 1.2.12 # Select target OS. TARGET must match a system for which COPTS and LIBS are # correctly defined below. diff --git a/TODO b/TODO index b06ce74b4..1471c4b1b 100644 --- a/TODO +++ b/TODO @@ -77,7 +77,7 @@ ok> - les options des serveurs ? - les filtres et regex ? -5) implémenter "balance source" pour faire un hash sur la source. +* implémenter "balance source" pour faire un hash sur la source. permettre de spécifier un masque sur lequel s'applique le hachage, ainsi qu'une option pour hacher en fonction de l'adresse dans le champ "x-forwarded-for". Problème pour le support des pannes: ce @@ -138,7 +138,7 @@ Todo for 1.2 * listen [ip6::...ip6]/port[-port] - server xxx ipv4 | ipv4: | ipv4:port[-port] | ipv6/ | ipv6/port[-port] * appcookie -- weighted round robin +* weighted round robin - option to shutdown(listen_sock) when max connections reached * epoll - replace the event scheduler with an O(log(N)) one @@ -149,4 +149,5 @@ Todo for 1.2 activity on the buffer's pointers from touching the buffer page itself. - make buffer size configurable in global options * monitor number of simultaneous sessions in logs (per srv/inst/global) +* ignore leading empty lines in HTTP requests diff --git a/doc/haproxy-en.txt b/doc/haproxy-en.txt index 335cff842..59cba6869 100644 --- a/doc/haproxy-en.txt +++ b/doc/haproxy-en.txt @@ -2,9 +2,9 @@ H A - P r o x y Reference Manual ------------------- - version 1.2.9 + version 1.2.12 willy tarreau - 2006/03/01 + 2006/04/15 ============ | Abstract | @@ -42,11 +42,14 @@ There are only a few command line options : exits with code 1 if a syntax error was found. -p asks the process to write down each of its children's pids to this file in daemon mode. + -sf specifies a list of pids to send a FINISH signal to after startup. + -st specifies a list of pids to send a TERMINATE signal to after startup. -s shows statistics (only if compiled in) -l shows even more statistics (implies '-s') -de disables use of epoll() -dp disables use of poll() - + -db disables background mode (stays in foreground, useful for debugging) + -m enforces a memory usage limit to a maximum of megabytes. The maximal number of connections per proxy is used as the default parameter for each instance for which the 'maxconn' paramter is not set in the 'listen' section. @@ -59,9 +62,24 @@ section. When the proxy runs in this mode, it dumps every connections, disconnections, timestamps, and HTTP headers to stdout. This should NEVER be used in an init script since it will prevent the system from starting up. +For debugging, the '-db' option is very useful as it temporarily disables +daemon mode and multi-process mode. The service can then be stopped by simply +pressing Ctrl-C, without having to edit the config nor run full debug. + Statistics are only available if compiled in with the 'STATTIME' option. It's only used during code optimization phases. +The '-st' and '-sf' options are used to inform previously running processes +that a configuration is being reloaded. They will receive the SIGTTOU signal to +ask them to temporarily stop listening to the ports so that the new process +can grab them. If anything wrong happens, the new process will send them a +SIGTTIN to tell them to re-listen to the ports and continue their normal +work. Otherwise, it will either ask them to finish (-sf) their work then softly +exit, or immediately terminate (-st), breaking existing sessions. A typical use +of this allows a configuration reload without service interruption : + + # haproxy -p /var/run/haproxy.pid -sf $(cat /var/run/haproxy.pid) + ====================== | Configuration file | ====================== @@ -219,7 +237,7 @@ Example : 1.4) Startup modes ------------------ -The service can start in several different : +The service can start in several different modes : - foreground / background - quiet / normal / debug @@ -230,6 +248,9 @@ returns immediately after forking. That's accomplished by the 'daemon' option in the 'global' section, which is the equivalent of the '-D' command line argument. +The '-db' command line argument overrides the 'daemon' and 'nbproc' global +options to make the process run in normal, foreground mode. + Moreover, certain alert messages are still sent to the standard output even in 'daemon' mode. To make them disappear, simply add the 'quiet' option in the 'global' section. This option has no command-line equivalent. @@ -282,6 +303,9 @@ Example : # to stop only those processes among others : # kill $( indique au processus père qu'il doit écrire les PIDs de ses fils dans ce fichier en mode démon. + -sf specifie une liste de PIDs auxquels envoyer un signal FINISH + -st specifie une liste de PIDs auxquels envoyer un signal TERMINATE -s affiche les statistiques (si option compilée) -l ajoute des informations aux statistiques -de désactive l'utilisation de epoll() -dp désactive l'utilisation de poll() + -db désactive la mise en arrière-plan (utile pour débugger) + -m applique une limitation de Mo d'utilisation mémoire Le nombre maximal de connexion simultanées par proxy est le paramètre par défaut pour les proxies pour lesquels ce paramètre n'est pas précisé dans le @@ -64,10 +68,27 @@ Le mode debug correspond mode, toutes les connexions, déconnexions, et tous les échanges d'en-têtes HTTP sont affichés. +Pour debugger, l'option '-db' est très pratique car elle désactive +temporairement le mode daemon et le mode multi-processus. Le service peut alors +être arrêté par un simple appui sur Ctrl-C, sans avoir à modifier la +configuration ni à activer le mode debug complet. + Les statistiques ne sont disponibles que si le programme a été compilé avec l'option "STATTIME". Il s'agit principalement de données brutes n'ayant d'utilité que lors de benchmarks par exemple. +Les paramètres '-st' et '-sf' sont utilisés pour informer des processus +existants que la configuration va être rechargée. Ils recevront le signal +SIGTTOU, leur demandant de libérer les ports en écoute afin que le nouveau +processus puisse les prendre. Si quoi que ce soit se passe mal, le nouveau +processus leur enverra un signal SIGTTIN pour leur indiquer qu'ils peuvent +se remettre en écoute et continuer leur travail. En revanche, si la +configuration se charge correctement, alors ils recevront un signal de demande +de fin de travail en douceur (-sf), ou de terminaison immédiate (-st) qui +coupera les sessions en cours. Un usage typique tel que celui-ci permet de +recharger une configuration sans interruption de service : + + # haproxy -p /var/run/haproxy.pid -sf $(cat /var/run/haproxy.pid) ============================ | Fichier de configuration | @@ -247,6 +268,9 @@ initialisation. Il faut le mettre en arri au processus appelant. C'est ce que fait l'option 'daemon' de la section 'global', et qui est l'équivalent du paramètre '-D' de la ligne de commande. +Le paramètre de ligne de commande '-db' inhibe les options globales 'daemon' +et 'nbproc' pour faire fonctionner le processus en mode normal, avant-plan. + Par ailleurs, certains messages d'alerte sont toujours envoyés sur la sortie standard, même en mode 'daemon'. Pour ne plus les voir ailleurs que dans les logs, il suffit de passer en mode silencieux par l'ajout de l'option 'quiet'. @@ -301,6 +325,9 @@ Exemple : # pour stopper seulement ces processus parmi d'autres : # kill $( OPTIONS / HTTP/1.0 - option httpchk URI -> OPTIONS HTTP/1.0 - option httpchk METH URI -> HTTP/1.0 - option httpchk METH URI VER -> + Voir les exemples ci-après. Depuis la version 1.1.17, il est possible de définir des serveurs de secours, @@ -1038,6 +1112,59 @@ tente de s'y connecter, il faut pr redispatch # renvoyer vers dispatch si serveur HS. +3.3) Assignation de poids différents à des serveurs +--------------------------------------------------- +Parfois il arrive d'ajouter de nouveaux serveurs pour accroître la capacité +d'une ferme de serveur, mais le nouveau serveur est soit beaucoup plus petit +que les autres (dans le cas d'un ajout d'urgence de matériel de récupération), +soit plus puissant (lors d'un investissement dans du matériel neuf). Pour cette +raison, il semble parfois judicieux de pouvoir envoyer plus de clients vers les +plus gros serveurs. Jusqu'à la version 1.2.11, il était nécessaire de répliquer +plusieurs fois les définitions des serveurs pour augmenter leur poids. Depuis +la version 1.2.12, l'option 'weight' est disponible. HAProxy construit alors +une vue des serveurs disponibles la plus homogène possible en se basant sur +leur poids de sorte que la charge se distribue de la manière la plus lisse +possible. Le poids compris entre 1 et 256 doit refléter la capacité d'un +serveur par rapport aux autres. De cette manière, si un serveur disparait, les +capacités restantes sont toujours respectées. + + +Exemple : +--------- +# distribution équitable sur 2 opteron and un ancien pentium3 + + listen web_appl 0.0.0.0:80 + mode http + cookie SERVERID insert nocache indirect + balance roundrobin + server pentium3-800 192.168.1.1:80 cookie server01 weight 8 check + server opteron-2.0G 192.168.1.2:80 cookie server02 weight 20 check + server opteron-2.4G 192.168.1.3:80 cookie server03 weight 24 check + server web-backup1 192.168.2.1:80 cookie server04 check backup + server web-excuse 192.168.3.1:80 check backup + +Notes : +------- + - lorsque le poids n'est pas spécifié, la valeur par défaut est à 1 + + - le poids n'impacte pas les tests de fonctionnement (health checks), donc il + est plus propre d'utiliser les poids que de répliquer le même serveur + plusieurs fois. + + - les poids s'appliquent également aux serveurs de backup si l'option + 'allbackups' est positionnée. + + - le poids s'applique aussi à la répartition selon la source + ('balance source'). + + - quels que soient les poids, le premier serveur sera toujours assigné en + premier. Cette règle facilite les diagnostics. + + - pour les puristes, l'algorithme de calculation de la vue des serveurs donne + une priorité aux premiers serveurs, donc la vue est la plus uniforme si les + serveurs sont déclarés dans l'ordre croissant de leurs poids. + + 4) Fonctionnalités additionnelles ================================= diff --git a/examples/haproxy-small.spec b/examples/haproxy-small.spec new file mode 100644 index 000000000..77ca08f49 --- /dev/null +++ b/examples/haproxy-small.spec @@ -0,0 +1,104 @@ +Summary: HA-Proxy is a TCP/HTTP reverse proxy for high availability environments +Name: haproxy +Version: 1.2.12 +Release: 1 +License: GPL +Group: System Environment/Daemons +URL: http://w.ods.org/tools/%{name}/ +Source0: http://w.ods.org/tools/%{name}/%{name}-%{version}.tar.gz +BuildRoot: %{_tmppath}/%{name}-%{version}-root +BuildRequires: pcre-devel +Requires: /sbin/chkconfig, /sbin/service + +%description +HA-Proxy is a TCP/HTTP reverse proxy which is particularly suited for high +availability environments. Indeed, it can: +- route HTTP requests depending on statically assigned cookies +- spread the load among several servers while assuring server persistence + through the use of HTTP cookies +- switch to backup servers in the event a main one fails +- accept connections to special ports dedicated to service monitoring +- stop accepting connections without breaking existing ones +- add/modify/delete HTTP headers both ways +- block requests matching a particular pattern + +It needs very little resource. Its event-driven architecture allows it to easily +handle thousands of simultaneous connections on hundreds of instances without +risking the system's stability. + +%prep +%setup -q + +%build +%{__make} REGEX="pcre" "COPTS.pcre=-DUSE_PCRE $(pcre-config --cflags)" DEBUG="" TARGET=linux24e SMALL_OPTS="-DBUFSIZE=8030 -DMAXREWRITE=1030 -DSYSTEM_MAXCONN=1024" DEBUG="" LIBS.pcre="-L\$(PCREDIR)/lib -Wl,-Bstatic -lpcreposix -lpcre -Wl,-Bdynamic" + +%install +[ "%{buildroot}" != "/" ] && %{__rm} -rf %{buildroot} + +%{__install} -d %{buildroot}%{_sbindir} +%{__install} -d %{buildroot}%{_sysconfdir}/rc.d/init.d +%{__install} -d %{buildroot}%{_sysconfdir}/%{name} + +%{__install} -s %{name} %{buildroot}%{_sbindir}/ +%{__install} -c -m 644 examples/%{name}.cfg %{buildroot}%{_sysconfdir}/%{name}/ +%{__install} -c -m 755 examples/%{name}.init %{buildroot}%{_sysconfdir}/rc.d/init.d/%{name} + +%clean +[ "%{buildroot}" != "/" ] && %{__rm} -rf %{buildroot} + +%post +/sbin/chkconfig --add %{name} + +%preun +if [ $1 = 0 ]; then + /sbin/service %{name} stop >/dev/null 2>&1 || : + /sbin/chkconfig --del %{name} +fi + +%postun +if [ "$1" -ge "1" ]; then + /sbin/service %{name} condrestart >/dev/null 2>&1 || : +fi + +%files +%defattr(-,root,root) +%doc CHANGELOG TODO examples doc/haproxy-en.txt doc/haproxy-fr.txt doc/architecture.txt +%attr(0755,root,root) %{_sbindir}/%{name} +%dir %{_sysconfdir}/%{name} +%attr(0644,root,root) %config(noreplace) %{_sysconfdir}/%{name}/%{name}.cfg +%attr(0755,root,root) %config %{_sysconfdir}/rc.d/init.d/%{name} + +%changelog +* Wed Apr 15 2006 Willy Tarreau +- updated to 1.2.12 + +* Wed Mar 30 2006 Willy Tarreau +- updated to 1.2.11.1 + +* Wed Mar 19 2006 Willy Tarreau +- updated to 1.2.10 + +* Wed Mar 15 2006 Willy Tarreau +- updated to 1.2.9 + +* Sat Jan 22 2005 Willy Tarreau +- updated to 1.2.3 (1.1.30) + +* Sun Nov 14 2004 Willy Tarreau +- updated to 1.1.29 +- fixed path to config and init files +- statically linked PCRE to increase portability to non-pcre systems + +* Sun Jun 6 2004 Willy Tarreau +- updated to 1.1.28 +- added config check support to the init script + +* Tue Oct 28 2003 Simon Matter +- updated to 1.1.27 +- added pid support to the init script + +* Wed Oct 22 2003 Simon Matter +- updated to 1.1.26 + +* Thu Oct 16 2003 Simon Matter +- initial build diff --git a/examples/haproxy.spec b/examples/haproxy.spec index a9f4ed276..6e8658b7e 100644 --- a/examples/haproxy.spec +++ b/examples/haproxy.spec @@ -1,6 +1,6 @@ Summary: HA-Proxy is a TCP/HTTP reverse proxy for high availability environments Name: haproxy -Version: 1.2.11.1 +Version: 1.2.12 Release: 1 License: GPL Group: System Environment/Daemons @@ -71,6 +71,9 @@ fi %attr(0755,root,root) %config %{_sysconfdir}/rc.d/init.d/%{name} %changelog +* Wed Apr 15 2006 Willy Tarreau +- updated to 1.2.12 + * Wed Mar 30 2006 Willy Tarreau - updated to 1.2.11.1 diff --git a/haproxy.c b/haproxy.c index df6f5d664..14f68a80b 100644 --- a/haproxy.c +++ b/haproxy.c @@ -88,11 +88,11 @@ #include "include/appsession.h" #ifndef HAPROXY_VERSION -#define HAPROXY_VERSION "1.2.11.1" +#define HAPROXY_VERSION "1.2.12" #endif #ifndef HAPROXY_DATE -#define HAPROXY_DATE "2006/03/30" +#define HAPROXY_DATE "2006/04/15" #endif /* this is for libc5 for example */ @@ -511,6 +511,8 @@ struct server { int inter; /* time in milliseconds */ int result; /* 0 = connect OK, -1 = connect KO */ int curfd; /* file desc used for current test, or -1 if not in test */ + unsigned char uweight, eweight; /* user-specified weight-1, and effective weight-1 */ + unsigned int wscore; /* weight score, used during srv map computation */ struct proxy *proxy; /* the proxy this server belongs to */ }; @@ -577,8 +579,12 @@ 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, *cursrv; /* known servers, current server */ - int srv_act, srv_bck; /* # of servers */ + 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 */ 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 */ @@ -1807,77 +1813,101 @@ 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 inline void recount_servers(struct proxy *px) { +static void recount_servers(struct proxy *px) { struct server *srv; - px->srv_act = 0; px->srv_bck = 0; + px->srv_act = 0; px->srv_bck = px->tot_wact = px->tot_wbck = 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++; - else + px->tot_wbck += srv->eweight + 1; + } 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->cursrv will be updated + * If any server is found, it will be returned and px->srv_rr_idx 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) { - struct server *srv; - struct server *end; + if (px->srv_map_sz == 0) + return NULL; - 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; + 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++]; } @@ -1889,58 +1919,20 @@ 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) { - struct server *srv; + unsigned int h, l; - if (px->srv_act) { - unsigned int h, l; + if (px->srv_map_sz == 0) + return NULL; - 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--; - } + l = h = 0; + if (px->srv_act > 1) { + while ((l + sizeof (int)) <= len) { + h ^= ntohl(*(unsigned int *)(&addr[l])); + l += sizeof (int); } - /* note that theorically we should not get there */ + h %= px->srv_map_sz; } - - 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; + return px->srv_map[h]; } @@ -3311,6 +3303,32 @@ int process_cli(struct session *t) { if (ptr == req->h) { /* empty line, end of headers */ int line, len; + + /* + * first, let's check that it's not a leading empty line, in + * which case we'll ignore and remove it (according to RFC2616). + */ + if (req->h == req->data) { + /* to get a complete header line, we need the ending \r\n, \n\r, \r or \n too */ + if (ptr > req->r - 2) { + /* this is a partial header, let's wait for more to come */ + req->lr = ptr; + break; + } + + /* now we know that *ptr is either \r or \n, + * and that there are at least 1 char after it. + */ + if ((ptr[0] == ptr[1]) || (ptr[1] != '\r' && ptr[1] != '\n')) + req->lr = ptr + 1; /* \r\r, \n\n, \r[^\n], \n[^\r] */ + else + req->lr = ptr + 2; /* \r\n or \n\r */ + /* ignore empty leading lines */ + buffer_replace2(req, req->h, req->lr, NULL, 0); + req->h = req->lr; + continue; + } + /* we can only get here after an end of headers */ /* we'll have something else to do here : add new headers ... */ @@ -5310,6 +5328,7 @@ 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, @@ -5345,6 +5364,7 @@ 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, @@ -5375,6 +5395,7 @@ 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, @@ -6980,13 +7001,9 @@ int cfg_parse_listen(char *file, int linenum, char **args) { return -1; } - if (curproxy->srv == NULL) - curproxy->srv = newsrv; - else - curproxy->cursrv->next = newsrv; - curproxy->cursrv = newsrv; - - newsrv->next = NULL; + /* the servers are linked backwards first */ + newsrv->next = curproxy->srv; + curproxy->srv = newsrv; newsrv->proxy = curproxy; do_check = 0; @@ -7049,6 +7066,17 @@ int cfg_parse_listen(char *file, int linenum, char **args) { newsrv->state |= SRV_BACKUP; cur_arg ++; } + else if (!strcmp(args[cur_arg], "weight")) { + int w; + w = atol(args[cur_arg + 1]); + if (w < 1 || w > 256) { + Alert("parsing [%s:%d] : weight of server %s is not within 1 and 256 (%d).\n", + file, linenum, newsrv->id, w); + return -1; + } + newsrv->uweight = w - 1; + cur_arg += 2; + } else if (!strcmp(args[cur_arg], "check")) { global.maxsock++; do_check = 1; @@ -7065,7 +7093,7 @@ int cfg_parse_listen(char *file, int linenum, char **args) { cur_arg += 2; } else { - Alert("parsing [%s:%d] : server %s only supports options 'backup', 'cookie', 'check', 'inter', 'rise', 'fall', 'port' and 'source'.\n", + Alert("parsing [%s:%d] : server %s only supports options 'backup', 'cookie', 'check', 'inter', 'rise', 'fall', 'port', 'source', and 'weight'.\n", file, linenum, newsrv->id); return -1; } @@ -7789,7 +7817,6 @@ int readcfgfile(char *file) { } while (curproxy != NULL) { - curproxy->cursrv = NULL; if (curproxy->state == PR_STSTOPPED) { curproxy = curproxy->next; continue; @@ -7846,12 +7873,59 @@ int readcfgfile(char *file) { file, curproxy->id); cfgerr++; } - else { - while (newsrv != NULL) { - /* nothing to check for now */ - newsrv = newsrv->next; - } + } + + /* first, we will invert the servers list order */ + newsrv = NULL; + while (curproxy->srv) { + struct server *next; + + 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; + } } + + 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) @@ -8048,12 +8122,6 @@ void init(int argc, char **argv) { arg_mode |= MODE_DAEMON | MODE_QUIET; else if (*flag == 'q') arg_mode |= MODE_QUIET; -#if STATTIME > 0 - else if (*flag == 's') - arg_mode |= MODE_STATS; - else if (*flag == 'l') - arg_mode |= MODE_LOG; -#endif else if (*flag == 's' && (flag[1] == 'f' || flag[1] == 't')) { /* list of pids to finish ('f') or terminate ('t') */ @@ -8074,6 +8142,12 @@ void init(int argc, char **argv) { } } } +#if STATTIME > 0 + else if (*flag == 's') + arg_mode |= MODE_STATS; + else if (*flag == 'l') + arg_mode |= MODE_LOG; +#endif else { /* >=2 args */ argv++; argc--; if (argc == 0)