* released 1.2.5 (1.1.31)

* changed the runtime argument to disable epoll() to '-de'
* changed the runtime argument to disable poll() to '-dp'
* added global options 'nopoll' and 'noepoll' to do the same at the
  configuration level.
* added a 'linux24e' target to the Makefile for Linux 2.4 systems patched to
  support epoll().
* changed default FD_SETSIZE to 65536 on Solaris (default=1024)
* conditionned signals redirection to #ifdef DEBUG_MEMORY
This commit is contained in:
willy tarreau 2005-12-18 01:13:11 +01:00
parent 1c2ad21e0f
commit 64a3cc3660
6 changed files with 326 additions and 182 deletions

View File

@ -1,6 +1,16 @@
ChangeLog :
===========
2005/04/30 : 1.2.5 (1.1.31)
- changed the runtime argument to disable epoll() to '-de'
- changed the runtime argument to disable poll() to '-dp'
- added global options 'nopoll' and 'noepoll' to do the same at the
configuration level.
- added a 'linux24e' target to the Makefile for Linux 2.4 systems patched to
support epoll().
- changed default FD_SETSIZE to 65536 on Solaris (default=1024)
- conditionned signals redirection to #ifdef DEBUG_MEMORY
2005/04/26 : 1.2.5-pre4
- made epoll() support a compile-time option : ENABLE_EPOLL
- provided a very little libc replacement for a possibly missing epoll()

View File

@ -6,6 +6,7 @@
# correctly defined below.
#TARGET = linux26
TARGET = linux24
#TARGET = linux24e
#TARGET = linux22
#TARGET = solaris
#TARGET = openbsd
@ -28,11 +29,15 @@ LD = gcc
PCREDIR := $(shell pcre-config --prefix 2>/dev/null || :)
#PCREDIR=/usr/local
# This is for Linux 2.6 with netfilter and EPOLL
# This is for standard Linux 2.6 with netfilter and epoll()
COPTS.linux26 = -DNETFILTER -DENABLE_POLL -DENABLE_EPOLL
LIBS.linux26 =
# This is for Linux 2.4 with netfilter
# This is for enhanced Linux 2.4 with netfilter and epoll() patch
COPTS.linux24e = -DNETFILTER -DENABLE_POLL -DENABLE_EPOLL -DUSE_MY_EPOLL
LIBS.linux24e =
# This is for standard Linux 2.4 with netfilter but without epoll()
COPTS.linux24 = -DNETFILTER -DENABLE_POLL
LIBS.linux24 =
@ -41,7 +46,7 @@ COPTS.linux22 = -DUSE_GETSOCKNAME -DENABLE_POLL
LIBS.linux22 =
# This is for Solaris 8
COPTS.solaris = -fomit-frame-pointer -DSOLARIS -DENABLE_POLL
COPTS.solaris = -fomit-frame-pointer -DENABLE_POLL -DFD_SETSIZE=65536
LIBS.solaris = -lnsl -lsocket
# This is for OpenBSD 3.0
@ -63,13 +68,14 @@ COPTS.pcre=-DUSE_PCRE -I$(PCREDIR)/include
LIBS.pcre=-L$(PCREDIR)/lib -lpcreposix -lpcre
# you can enable debug arguments with "DEBUG=-g" or disable them with "DEBUG="
#DEBUG =
#DEBUG = -g -DDEBUG_MEMORY
DEBUG = -g
# if small memory footprint is required, you can reduce the buffer size. There
# are 2 buffers per concurrent session, so 16 kB buffers will eat 32 MB memory
# with 1000 concurrent sessions.
#SMALL_OPTS = -DBUFSIZE=8192 -DMAXREWRITE=1024
# with 1000 concurrent sessions. Putting it slightly lower than a page size
# will avoid the additionnal paramters to overflow a page.
#SMALL_OPTS = -DBUFSIZE=8100 -DMAXREWRITE=1000
SMALL_OPTS =
# redefine this if you want to add some special PATH to include/libs

7
TODO
View File

@ -141,3 +141,10 @@ Todo for 1.2
- option to shutdown(listen_sock) when max connections reached
* epoll
- replace the event scheduler with an O(log(N)) one
- refine memory management so that the request buffer is only allocated in
cli_read() and response buffer during srv_read(). This would protect against
attacks with thousands connections : 20000 connections consume 340 MB RSS and
1.3 GB VSZ on Linux. Data should be in a separate buffer to prevent any
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)

View File

@ -4,7 +4,7 @@
-------------------
version 1.2.5
willy tarreau
2005/04/24
2005/04/30
============
| Abstract |
@ -44,6 +44,8 @@ There are only a few command line options :
pids to this file in daemon mode.
-s shows statistics (only if compiled in)
-l shows even more statistics (implies '-s')
-de disables use of epoll()
-dp disables use of poll()
The maximal number of connections per proxy is used as the default parameter for
@ -98,6 +100,8 @@ the following ones :
- nbproc <number>
- daemon
- debug
- noepoll
- nopoll
- quiet
- pidfile <file>
- stats
@ -264,6 +268,43 @@ Example :
# kill $(</var/run/haproxy-private.pid)
1.7) Polling mechanisms
-----------------------
Starting from version 1.2.5, haproxy supports the poll() and epoll() polling
mechanisms. On systems where select() is limited by FD_SETSIZE (like Solaris),
poll() can be an interesting alternative. Performance tests show that Solaris'
poll() performance does not decay as fast as the numbers of sockets increase,
making it a safe solution for high loads. However, Solaris already uses poll()
to emulate select(), so as long as the number of sockets has no reason to go
higher than FD_SETSIZE, poll() should not provide any better performance. On
Linux systems with the epoll() patch (or any 2.6 version), haproxy will use
epoll() which is extremely fast and non dependant on the number of sockets.
Tests have shown constant performance from 1 to 20000 simultaneous sessions.
Haproxy will use epoll() when available, and will fall back to poll(), then to
select(). However, if for any reason you need to disable epoll() or poll() (eg.
because of a bug or just to compare performance), two new global options have
been created for this matter : 'noepoll' and 'nopoll'.
Example :
---------
global
# use only select()
noepoll
nopoll
Note :
------
For the sake of configuration file portability, these options are accepted but
ignored if the poll() or epoll() mechanisms have not been enabled at compile
time.
To make debugging easier, the '-de' runtime argument disables epoll support and
the '-dp' argument disables poll support. They are respectively equivalent to
'noepoll' and 'nopoll'.
2) Declaration of a listening service
=====================================

View File

@ -4,7 +4,7 @@
-------------------
version 1.2.5
willy tarreau
2005/04/24
2005/04/30
================
| Introduction |
@ -13,8 +13,8 @@
HA-Proxy est un relais TCP/HTTP offrant des facilités d'intégration en
environnement hautement disponible. En effet, il est capable de :
- effectuer un aiguillage statique défini par des cookies ;
- effectuer une répartition de charge avec création de cookies pour assurer la
persistence de session ;
- effectuer une répartition de charge avec création de cookies pour assurer
la persistence de session ;
- fournir une visibilité externe de son état de santé ;
- s'arrêter en douceur sans perte brutale de service ;
- modifier/ajouter/supprimer des entêtes dans la requête et la réponse ;
@ -22,9 +22,9 @@ environnement hautement disponible. En effet, il est capable de :
- utiliser des serveurs de secours lorsque les serveurs principaux sont hors
d'usage.
Il requiert peu de ressources, et son architecture événementielle mono-processus
lui permet facilement de gérer plusieurs milliers de connexions simultanées sur
plusieurs relais sans effondrer le système.
Il requiert peu de ressources, et son architecture événementielle mono-
processus lui permet facilement de gérer plusieurs milliers de connexions
simultanées sur plusieurs relais sans effondrer le système.
===========================
@ -48,14 +48,17 @@ Les options de lancement sont peu nombreuses :
fils dans ce fichier en mode démon.
-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()
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 fichier de
configuration. Il s'agit du paramètre 'maxconn' dans les sections 'listen'.
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
fichier de configuration. Il s'agit du paramètre 'maxconn' dans les sections
'listen'.
Le nombre maximal total de connexions simultanées limite le nombre de connexions
TCP utilisables à un instant donné par le processus, tous proxies confondus. Ce
paramètre remplace le paramètre 'maxconn' de la section 'global'.
Le nombre maximal total de connexions simultanées limite le nombre de
connexions TCP utilisables à un instant donné par le processus, tous proxies
confondus. Ce paramètre remplace le paramètre 'maxconn' de la section 'global'.
Le mode debug correspond à l'option 'debug' de la section 'global'. Dans ce
mode, toutes les connexions, déconnexions, et tous les échanges d'entêtes HTTP
@ -74,8 +77,8 @@ Structure
=========
L'analyseur du fichier de configuration ignore des lignes vides, les espaces,
les tabulations, et tout ce qui est compris entre le symbole '#' (s'il n'est pas
précédé d'un '\'), et la fin de la ligne, ce qui constitue un commentaire.
les tabulations, et tout ce qui est compris entre le symbole '#' (s'il n'est
pas précédé d'un '\'), et la fin de la ligne, ce qui constitue un commentaire.
Le fichier de configuration est découpé en sections répérées par des mots clés
tels que :
@ -103,6 +106,8 @@ support
- nbproc <nombre>
- daemon
- debug
- noepoll
- nopoll
- quiet
- pidfile <fichier>
@ -117,9 +122,9 @@ syslog vers un ou deux serveurs. La syntaxe est la suivante :
Les connexions sont envoyées en niveau "info". Les démarrages de service et de
serveurs seront envoyés en "notice", les signaux d'arrêts en "warning" et les
arrêts définitifs de services et de serveurs en "alert". Ceci est valable aussi
bien pour les proxies que pour les serveurs testés par les proxies. Le paramètre
optionnel <niveau_max> définit le niveau maximal de traces émises parmi les 8
valeurs suivantes :
bien pour les proxies que pour les serveurs testés par les proxies. Le
paramètre optionnel <niveau_max> définit le niveau maximal de traces émises
parmi les 8 valeurs suivantes :
emerg, alert, crit, err, warning, notice, info, debug
Par compatibilité avec les versions 1.1.16 et antérieures, la valeur par défaut
@ -152,8 +157,8 @@ de sockets n
- 1 socket pour chaque serveur en cours de health-check
- 1 socket pour les logs (tous serveurs confondus)
Dans le cas où chaque proxy n'écoute que sur un couple adresse/port, positionner
la limite du nombre de descripteurs de fichiers (ulimit -n) à
Dans le cas où chaque proxy n'écoute que sur un couple adresse/port,
positionner la limite du nombre de descripteurs de fichiers (ulimit -n) à
(2 * maxconn + nbproxy + nbserveurs + 1). Dans une future version, haproxy sera
capable de positionner lui-même cette limite.
@ -184,20 +189,23 @@ plusieurs services de nature diff
il est conseillé d'utiliser un répertoire vide, sans aucun droit, et de changer
l'uid du processus de sorte qu'il ne puisse rien faire dans ledit répertoire.
Remarque: dans le cas où une telle faille serait mise en évidence, il est fort
probable que les premières tentatives de son exploitation provoquent un arrêt du
Remarque importante :
---------------------
Dans le cas où une telle faille serait mise en évidence, il est fort probable
que les premières tentatives de son exploitation provoquent un arrêt du
programme, à cause d'un signal de type 'Segmentation Fault', 'Bus Error' ou
encore 'Illegal Instruction'. Même s'il est vrai que faire tourner le serveur en
environnement limité réduit les risques d'intrusion, il est parfois bien utile
dans ces circonstances de connaître les conditions d'apparition du problème, via
l'obtention d'un fichier 'core'. La plupart des systèmes, pour des raisons de
sécurité, désactivent la génération du fichier 'core' après un changement
d'identifiant pour le processus. Il faudra donc soit lancer le processus à
partir d'un compte utilisateur aux droits réduits (mais ne pouvant pas effectuer
le chroot), ou bien le faire en root sans réduction des droits (uid 0). Dans ce
cas, le fichier se trouvera soit dans le répertoire de lancement, soit dans le
répertoire spécifié après l'option 'chroot'. Ne pas oublier la commande suivante
pour autoriser la génération du fichier avant de lancer le programme :
encore 'Illegal Instruction'. Même s'il est vrai que faire tourner le serveur
en environnement limité réduit les risques d'intrusion, il est parfois bien
utile dans ces circonstances de connaître les conditions d'apparition du
problème, via l'obtention d'un fichier 'core'. La plupart des systèmes, pour
des raisons de sécurité, désactivent la génération du fichier 'core' après un
changement d'identifiant pour le processus. Il faudra donc soit lancer le
processus à partir d'un compte utilisateur aux droits réduits (mais ne pouvant
pas effectuer le chroot), ou bien le faire en root sans réduction des droits
(uid 0). Dans ce cas, le fichier se trouvera soit dans le répertoire de
lancement, soit dans le répertoire spécifié après l'option 'chroot'. Ne pas
oublier la commande suivante pour autoriser la génération du fichier avant de
lancer le programme :
# ulimit -c unlimited
@ -215,9 +223,9 @@ Le service peut fonctionner dans plusieurs modes :
- avant- / arrière-plan
- silencieux / normal / debug
Le mode par défaut est normal, avant-plan, c'est à dire que le programme ne rend
pas la main une fois lancé. Il ne faut surtout pas le lancer comme ceci dans un
script de démarrage du système, sinon le système ne finirait pas son
Le mode par défaut est normal, avant-plan, c'est à dire que le programme ne
rend pas la main une fois lancé. Il ne faut surtout pas le lancer comme ceci
dans un script de démarrage du système, sinon le système ne finirait pas son
initialisation. Il faut le mettre en arrière-plan, de sorte qu'il rende la main
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.
@ -277,6 +285,44 @@ Exemple :
# kill $(</var/run/haproxy-private.pid)
1.7) Mécanismes de traitements des événements
---------------------------------------------
A partir de la version 1.2.5, haproxy supporte les mécanismes poll() et
epoll(). Sur les systems où select() est limité par FD_SETSIZE (comme Solaris),
poll() peut être une alternative intéressante. Des tests de performance
montrent que les performances de poll() ne décroissent pas aussi vite que le
nombre de sockets augmente, ce qui en fait une solution sûre pour les fortes
charges. Cela dit, Soalris utilise déjà poll() pour émuler select(), donc tant
que le nombre de sockets ne dépasse pas FD_SETSIZE, poll() ne devrait pas
apporter de performances supplémentaires. Sur les systèmes à base Linux
incluant le patch epoll() (ou tous les Linux 2.6), haproxy utilisera epoll()
qui est extrèmement rapide indépendamment du nombre de sockets. Les tests sur
haproxy ont montré une performance constante de 1 à 40000 sessions simultanées.
Haproxy utilisera epoll() lorsqu'il est disponible, et se repliera sur poll(),
puis en dernier lieu sur select(). Cependant, si pour une raison quelconque il
s'avérait nécessaire de désactiver epoll() ou poll() (p.ex: à cause d'un bug ou
juste pour comparer les performances), deux nouvelles options globales ont été
ajoutées dans ce but : 'noepoll' et 'nopoll'.
Exemple :
---------
global
# utiliser seulement select()
noepoll
nopoll
Remarque :
----------
Dans le but d'assurer une portabilité maximale des configurations, ces options
sont acceptées et ignorées si les mécanismes poll() ou epoll() n'ont pas été
activés lors de la compilation.
Afin de simplifier la résolution de problèmes, le paramètre '-de' en ligne de
commande désactive epoll() et le paramètre '-dp' désactive poll(). Ils sont
respectivement équivalents à 'noepoll' et 'nopoll'.
2) Définition d'un service en écoute
====================================
@ -285,9 +331,9 @@ Les sections de service d
listen <nom_instance> [ <adresse_IP>:<plage_ports>[,...] ]
- <nom_instance> est le nom de l'instance décrite. Ce nom sera envoyé dans les
logs, donc il est souhaitable d'utiliser un nom relatif au service relayé. Aucun
test n'est effectué concernant l'unicité de ce nom, qui n'est pas obligatoire,
mais fortement recommandée.
logs, donc il est souhaitable d'utiliser un nom relatif au service relayé.
Aucun test n'est effectué concernant l'unicité de ce nom, qui n'est pas
obligatoire, mais fortement recommandée.
- <adresse_IP> est l'adresse IP sur laquelle le relais attend ses connexions.
L'absence d'adresse ainsi que l'adresse 0.0.0.0 signifient que les connexions
@ -304,8 +350,8 @@ Les sections de service d
<adresse_IP>:<port> consomme une socket, donc un descripteur de fichier.
Le couple <adresse_IP>:<port> doit être unique pour toutes les instances
d'une même machine. L'attachement à un port inférieur à 1024 nécessite un
niveau de privilège particulier lors du lancement du programme (indépendamment
du paramètre 'uid' de la section 'global').
niveau de privilège particulier lors du lancement du programme
(indépendamment du paramètre 'uid' de la section 'global').
- le couple <adresse_IP>:<plage_ports> peut être répété indéfiniment pour
demander au relais d'écouter également sur d'autres adresses et/ou d'autres
@ -353,9 +399,10 @@ Un service peut fonctionner dans trois modes diff
Mode TCP
--------
Dans ce mode, le service relaye, dès leur établissement, les connexions TCP vers
un ou plusieurs serveurs. Aucun traitement n'est effectué sur le flux. Il s'agit
simplement d'une association source<adresse:port> -> destination<adresse:port>.
Dans ce mode, le service relaye, dès leur établissement, les connexions TCP
vers un ou plusieurs serveurs. Aucun traitement n'est effectué sur le flux. Il
s'agit simplement d'une association
source<adresse:port> -> destination<adresse:port>.
Pour l'utiliser, préciser le mode TCP sous la déclaration du relais.
Exemple :
@ -414,15 +461,19 @@ Exemple :
2.4) Arrêt en douceur
---------------------
Il est possible d'arrêter les services en douceur en envoyant un signal SIG_USR1
au processus relais. Tous les services seront alors mis en phase d'arrêt, mais
pourront continuer d'accepter des connexions pendant un temps défini par le
paramètre 'grace' (en millisecondes). Cela permet par exemple, de faire savoir
rapidement à un répartiteur de charge qu'il ne doit plus utiliser un relais,
tout en continuant d'assurer le service le temps qu'il s'en rende compte.
Remarque : les connexions actives ne sont jamais cassées. Dans le pire des cas,
il faudra attendre en plus leur expiration avant l'arrêt total du processus. La
valeur par défaut est 0 (pas de grâce, arrêt immédiat de l'écoute).
Il est possible d'arrêter les services en douceur en envoyant un signal
SIG_USR1 au processus relais. Tous les services seront alors mis en phase
d'arrêt, mais pourront continuer d'accepter des connexions pendant un temps
défini par le paramètre 'grace' (en millisecondes). Cela permet par exemple,
de faire savoir rapidement à un répartiteur de charge qu'il ne doit plus
utiliser un relais, tout en continuant d'assurer le service le temps qu'il
s'en rende compte.
Remarque :
----------
Les connexions actives ne sont jamais cassées. Dans le pire des cas, il faudra
attendre en plus leur expiration avant l'arrêt total du processus. La valeur
par défaut est 0 (pas de grâce, arrêt immédiat de l'écoute).
Exemple :
---------
@ -468,11 +519,12 @@ Remarques :
- "contimeout" et "srvtimeout" n'ont pas d'utilité dans le cas du serveur de
type "health".
- sous de fortes charges, ou sur un réseau saturé ou défectueux, il est
possible de perdre des paquets. Du fait que la première retransmission TCP
n'ait lieu qu'au bout de 3 secoudes, fixer un timeout de connexion inférieur
à 3 secondes ne permet pas de se rattraper sur la perte de paquets car la
session aura été abandonnée avant la première retransmission. Une valeur de
4 secondes réduira considérablement le nombre d'échecs de connexion.
possible de perdre des paquets. Du fait que la première retransmission
TCP n'ait lieu qu'au bout de 3 secoudes, fixer un timeout de connexion
inférieur à 3 secondes ne permet pas de se rattraper sur la perte
de paquets car la session aura été abandonnée avant la première
retransmission. Une valeur de 4 secondes réduira considérablement
le nombre d'échecs de connexion.
2.6) Tentatives de reconnexion
------------------------------
@ -514,12 +566,12 @@ Il est possible de forcer l'adresse utilis
les serveurs à l'aide du paramètre "source". Il est même possible de forcer le
port, bien que cette fonctionnalité se limite à des usages très spécifiques.
C'est particulièrement utile en cas d'adressage multiple, et plus généralement
pour permettre aux serveurs de trouver le chemin de retour dans des contextes de
routage difficiles. Si l'adresse est '0.0.0.0' ou '*' ou vide, elle sera choisie
librement par le systeme. Si le port est '0' ou vide, il sera choisi librement
par le système. Il est à noter que depuis la version 1.1.18, les tests de bon
fonctionnement des serveurs seront aussi effectués à partir de la source
spécifiée par ce paramètre.
pour permettre aux serveurs de trouver le chemin de retour dans des contextes
de routage difficiles. Si l'adresse est '0.0.0.0' ou '*' ou vide, elle sera
choisie librement par le systeme. Si le port est '0' ou vide, il sera choisi
librement par le système. Il est à noter que depuis la version 1.1.18, les
tests de bon fonctionnement des serveurs seront aussi effectués à partir de la
source spécifiée par ce paramètre.
Exemples :
----------
@ -565,9 +617,9 @@ lors d'un acc
cookie SERVERID rewrite
Pour créer un cookie comportant la valeur attribuée à un serveur lors d'un accès
en répartition de charge interne. Dans ce cas, il est souhaitable que tous les
serveurs aient un cookie renseigné. Un serveur non assigné d'un cookie
Pour créer un cookie comportant la valeur attribuée à un serveur lors d'un
accès en répartition de charge interne. Dans ce cas, il est souhaitable que
tous les serveurs aient un cookie renseigné. Un serveur non assigné d'un cookie
retournera un cookie vide (cookie de suppression) :
cookie SERVERID insert
@ -585,17 +637,17 @@ ajouter le mot cl
cookie SERVERID insert nocache
Pour insérer un cookie seulement suite aux requêtes de type POST, ajouter le mot
clé 'postonly' après 'insert' :
Pour insérer un cookie seulement suite aux requêtes de type POST, ajouter le
mot clé 'postonly' après 'insert' :
cookie SERVERID insert postonly
Remarques :
-----------
- Il est possible de combiner 'insert' avec 'indirect' ou 'rewrite' pour s'adapter
à des applications générant déjà le cookie, avec un contenu invalide. Il suffit
pour cela de les spécifier sur la même ligne.
- Il est possible de combiner 'insert' avec 'indirect' ou 'rewrite' pour
s'adapter à des applications générant déjà le cookie, avec un contenu
invalide. Il suffit pour cela de les spécifier sur la même ligne.
- dans le cas où 'insert' et 'indirect' sont spécifiés, le cookie n'est jamais
transmis au serveur vu qu'il n'en a pas connaissance et ne pourrait pas le
@ -719,35 +771,36 @@ Exemples :
3.1) Surveillance des serveurs
------------------------------
Il est possible de tester l'état des serveurs par établissement de connexion TCP
ou par envoi d'une requête HTTP. Un serveur hors d'usage ne sera pas utilisé
dans le processus de répartition de charge interne. Pour activer la surveillance,
ajouter le mot clé 'check' à la fin de la déclaration du serveur. Il est
possible de spécifier l'intervalle (en millisecondes) séparant deux tests du
serveur par le paramètre "inter", le nombre d'échecs acceptés par le paramètre
"fall", et le nombre de succès avant reprise par le paramètre "rise". Les
paramètres non précisés prennent les valeurs suivantes par défaut :
Il est possible de tester l'état des serveurs par établissement de connexion
TCP ou par envoi d'une requête HTTP. Un serveur hors d'usage ne sera pas
utilisé dans le processus de répartition de charge interne. Pour activer la
surveillance, ajouter le mot clé 'check' à la fin de la déclaration du serveur.
Il est possible de spécifier l'intervalle (en millisecondes) séparant deux
tests du serveur par le paramètre "inter", le nombre d'échecs acceptés par le
paramètre "fall", et le nombre de succès avant reprise par le paramètre "rise".
Les paramètres non précisés prennent les valeurs suivantes par défaut :
- inter : 2000
- rise : 2
- fall : 3
- port : port de connexion du serveur
Le mode par défaut consiste à établir des connexions TCP uniquement. Dans
certains cas de pannes, des serveurs peuvent continuer à accepter les connexions
sans les traiter. Depuis la version 1.1.16, haproxy est en mesure d'envoyer des
requêtes HTTP courtes et très peu coûteuses. Les versions 1.1.16 et 1.1.17
utilisent "OPTIONS / HTTP/1.0". Dans les versions 1.1.18 à 1.1.20, les requêtes
ont été changées en "OPTIONS * HTTP/1.0" pour des raisons de contrôle d'accès aux
ressources. Cependant, cette requête documentée dans la RFC2068 n'est pas
comprise par tous les serveurs. Donc à partir de la version 1.1.21, la requête
par défaut est revenue à "OPTIONS / HTTP/1.0", mais il est possible de
paramétrer la partie URI. Les requêtes OPTIONS présentent l'avantage d'être
facilement extractibles des logs, et de ne pas induire d'accès aux fichiers côté
serveur. Seules les réponses 2xx et 3xx sont considérées valides, les autres (y
compris non-réponses) aboutissent à un échec. Le temps maximal imparti pour une
réponse est égal à l'intervalle entre deux tests (paramètre "inter"). Pour
activer ce mode, spécifier l'option "httpchk", éventuellement suivie d'une
méthode et d'une URI. L'option "httpchk" accepte donc 4 formes :
certains cas de pannes, des serveurs peuvent continuer à accepter les
connexions sans les traiter. Depuis la version 1.1.16, haproxy est en mesure
d'envoyer des requêtes HTTP courtes et très peu coûteuses. Les versions 1.1.16
et 1.1.17 utilisent "OPTIONS / HTTP/1.0". Dans les versions 1.1.18 à 1.1.20,
les requêtes ont été changées en "OPTIONS * HTTP/1.0" pour des raisons de
contrôle d'accès aux ressources. Cependant, cette requête documentée dans la
RFC2068 n'est pas comprise par tous les serveurs. Donc à partir de la version
1.1.21, la requête par défaut est revenue à "OPTIONS / HTTP/1.0", mais il est
possible de paramétrer la partie URI. Les requêtes OPTIONS présentent
l'avantage d'être facilement extractibles des logs, et de ne pas induire
d'accès aux fichiers côté serveur. Seules les réponses 2xx et 3xx sont
considérées valides, les autres (y compris non-réponses) aboutissent à un
échec. Le temps maximal imparti pour une réponse est égal à l'intervalle entre
deux tests (paramètre "inter"). Pour activer ce mode, spécifier l'option
"httpchk", éventuellement suivie d'une méthode et d'une URI. L'option "httpchk"
accepte donc 4 formes :
- option httpchk -> OPTIONS / HTTP/1.0
- option httpchk URI -> OPTIONS <URI> HTTP/1.0
- option httpchk METH URI -> <METH> <URI> HTTP/1.0
@ -772,12 +825,12 @@ pour les configurations o
lorsqu'il est déduit du port d'acceptation de la connexion. Pour cela, utiliser
le paramètre 'port' suivi du numéro de port devant répondre aux requêtes.
Enfin, depuis la version 1.1.17, il est possible de visualiser rapidement l'état
courant de tous les serveurs. Pour cela, il suffit d'envoyer un signal SIGHUP au
processus proxy. L'état de tous les serveurs de tous les proxies est envoyé dans
les logs en niveau "notice", ainsi que sur la sortie d'erreurs si elle est
active. C'est une bonne raison pour avoir au moins un serveur de logs local en
niveau notice.
Enfin, depuis la version 1.1.17, il est possible de visualiser rapidement
l'état courant de tous les serveurs. Pour cela, il suffit d'envoyer un signal
SIGHUP au processus proxy. L'état de tous les serveurs de tous les proxies est
envoyé dans les logs en niveau "notice", ainsi que sur la sortie d'erreurs si
elle est active. C'est une bonne raison pour avoir au moins un serveur de logs
local en niveau notice.
Depuis la version 1.1.18 (et 1.2.1), un message d'urgence est envoyé dans les
logs en niveau 'emerg' si tous les serveurs d'une même instance sont tombés,
@ -890,12 +943,12 @@ Exemple :
server web2 192.168.1.2:80 cookie server02
redispatch # renvoyer vers dispatch si refus de connexion.
Par défaut (et dans les versions 1.1.16 et antérieures), le paramètre redispatch
ne s'applique qu'aux échecs de connexion au serveur. Depuis la version 1.1.17,
il s'applique aussi aux connexions destinées à des serveurs identifiés comme
hors d'usage par la surveillance. Si l'on souhaite malgré tout qu'un client
disposant d'un cookie correspondant à un serveur défectueux tente de s'y
connecter, il faut préciser l'option "persist" :
Par défaut (et dans les versions 1.1.16 et antérieures), le paramètre
redispatch ne s'applique qu'aux échecs de connexion au serveur. Depuis la
version 1.1.17, il s'applique aussi aux connexions destinées à des serveurs
identifiés comme hors d'usage par la surveillance. Si l'on souhaite malgré
tout qu'un client disposant d'un cookie correspondant à un serveur défectueux
tente de s'y connecter, il faut préciser l'option "persist" :
listen http_proxy 0.0.0.0:80
mode http
@ -918,9 +971,9 @@ la r
---------------------------
4.1.1) Fonctionnement en mode transparent
---------------------------------------
En mode HTTP, le mot clé 'transparent' permet d'intercepter des sessions routées
à travers la machine hébergeant le proxy. Dans ce mode, on ne précise pas
l'adresse de répartition 'dispatch', car celle-ci est tirée de l'adresse
En mode HTTP, le mot clé 'transparent' permet d'intercepter des sessions
routées à travers la machine hébergeant le proxy. Dans ce mode, on ne précise
pas l'adresse de répartition 'dispatch', car celle-ci est tirée de l'adresse
destination de la session détournée. Le système doit permettre de rediriger les
paquets vers un processus local.
@ -938,9 +991,10 @@ Exemple :
Remarque :
----------
Si le port n'est pas spécifié sur le serveur, c'est le port auquel s'est adressé
le client qui sera utilisé. Cela permet de relayer tous les ports TCP d'une même
adresse avec une même instance et sans utiliser directement le mode transparent.
Si le port n'est pas spécifié sur le serveur, c'est le port auquel s'est
adressé le client qui sera utilisé. Cela permet de relayer tous les ports TCP
d'une même adresse avec une même instance et sans utiliser directement le mode
transparent.
Exemple :
---------
@ -1067,16 +1121,16 @@ Exemple :
Le problème de loguer uniquement en fin de session, c'est qu'il est impossible
de savoir ce qui se passe durant de gros transferts ou des sessions longues.
Pour pallier à ce problème, une nouvelle option 'logasap' a été introduite dans
la version 1.1.28 (1.2.1). Lorsqu'elle est activée, le proxy loguera le plus tôt
possible, c'est à dire juste avant que ne débutent les transferts de données.
Cela signifie, dans le cas du TCP, qu'il loguera toujours le résultat de la
connexion vers le serveur, et dans le cas HTTP, qu'il loguera en fin de
la version 1.1.28 (1.2.1). Lorsqu'elle est activée, le proxy loguera le plus
tôt possible, c'est à dire juste avant que ne débutent les transferts de
données. Cela signifie, dans le cas du TCP, qu'il loguera toujours le résultat
de la connexion vers le serveur, et dans le cas HTTP, qu'il loguera en fin de
traitement des entêtes de la réponse du serveur, auquel cas le nombre d'octets
représentera la taille des entêtes retournés au client.
Afin d'éviter toute confusion avec les logs normaux, le temps total de transfert
et le nombre d'octets transférés sont préfixés d'un signe '+' rappeleant que les
valeurs réelles sont certainement plus élevées.
Afin d'éviter toute confusion avec les logs normaux, le temps total de
transfert et le nombre d'octets transférés sont préfixés d'un signe '+'
rappeleant que les valeurs réelles sont certainement plus élevées.
Exemple :
---------
@ -1167,9 +1221,9 @@ Autres cas ('xx' repr
4.2.4) Conditions de déconnexion
--------------------------------
Les logs TCP et HTTP fournissent un indicateur de complétude de la session.
C'est un champ de 4 caractères (2 en TCP) précédant la requête HTTP, indiquant :
- sur le premier caractère, un code précisant le premier événement qui a causé
la terminaison de la session :
C'est un champ de 4 caractères (2 en TCP) précédant la requête HTTP, indiquant:
- sur le premier caractère, un code précisant le premier événement qui a
causé la terminaison de la session :
C : fermeture de la session TCP de la part du client
S : fermeture de la session TCP de la part du serveur, ou refus de connexion
@ -1206,8 +1260,8 @@ C'est un champ de 4 caract
serveur correspondant.
- : non appliquable
- le dernier caractère indique l'éventuel traitement effectué sur un cookie de
persistence retrourné par le serveur (uniquement en mode HTTP) :
- le dernier caractère indique l'éventuel traitement effectué sur un cookie
de persistence retrourné par le serveur (uniquement en mode HTTP) :
N : aucun cookie de persistence n'a été fourni par le serveur.
P : un cookie de persistence a été fourni par le serveur et transmis
@ -1241,9 +1295,9 @@ Exemples :
capture cookie vgnvisitor= len 32
Dans les logs, le champ précédant l'indicateur de complétude contient le cookie
positionné par le serveur, précédé du cookie positionné par le client. Chacun de
ces champs est remplacé par le signe "-" lorsqu'aucun cookie n'est fourni par le
client ou le serveur.
positionné par le serveur, précédé du cookie positionné par le client. Chacun
de ces champs est remplacé par le signe "-" lorsqu'aucun cookie n'est fourni
par le client ou le serveur.
4.2.5) Exemples de logs
-----------------------
@ -1281,17 +1335,18 @@ client ou le serveur.
- haproxy[18989]: 10.0.0.1:34552 [15/Oct/2003:15:26:31] relais-http Srv1 3183/-1/-1/11215 503 0 - - SC-- "HEAD / HTTP/1.0"
=> La requête client met 3s à entrer (peut-être un problème réseau), et la
connexion ('SC--') vers le serveur échoue au bout de 4 tentatives de 2
secondes (retries 3 dans la conf), puis un code 503 est retourné au client.
secondes (retries 3 dans la conf), puis un code 503 est retourné au
client.
4.2.6) Caractères non-imprimables
---------------------------------
Depuis la version 1.1.29, les caractères non-imprimables ne sont plus envoyés
tels quels dans les lignes de logs, mais inscrits sous la forme de deux chiffres
hexadécimaux, préfixés du caractère d'échappement '#'. Les seuls caractères
dorénavant logués tels quels sont compris entre 32 et 126. Bien évidemment, le
caractère d'échappement '#' est lui-même encodé afin de lever l'ambiguité. Il en
est de même pour le caractère '"', ainsi que les caractères '{', '|' et '}' pour
les en-têtes.
tels quels dans les lignes de logs, mais inscrits sous la forme de deux
chiffres hexadécimaux, préfixés du caractère d'échappement '#'. Les seuls
caractères dorénavant logués tels quels sont compris entre 32 et 126. Bien
évidemment, le caractère d'échappement '#' est lui-même encodé afin de lever
l'ambiguité. Il en est de même pour le caractère '"', ainsi que les caractères
'{', '|' et '}' pour les en-têtes.
4.2.7) Journalisation d'en-têtes
--------------------------------
@ -1300,10 +1355,10 @@ d'en-t
requête que de la réponse. C'est particulièrement pratique pour savoir à quel
serveur virtuel une requête s'adressait, pour connaitre la longueur des données
émises lors d'un POST, ou encore loguer un identifiant unique de requête
positionné en amont. Dans la réponse, on peut chercher également à conserver des
informations relatives à la taille annoncée de la réponse, le fonctionnement
attendu du cache, ou encore la localisation d'un objet en cas de redirection.
La syntaxe est la suivante :
positionné en amont. Dans la réponse, on peut chercher également à conserver
des informations relatives à la taille annoncée de la réponse, le
fonctionnement attendu du cache, ou encore la localisation d'un objet en cas
de redirection. La syntaxe est la suivante :
capture request header <nom> len <longueur max>
capture response header <nom> len <longueur max>
@ -1322,12 +1377,13 @@ Exemples:
# noter l'URL de redirection
capture response header Location len 20
Les en-têtes non trouvés sont logués à vide, et si un en-tête apparait plusieurs
fois, seule la dernière occurence sera conservée. Les en-têtes de requête sont
regroupés entre deux accolades '{' et '}' dans l'ordre de leur déclaration, et
chacun séparés par une barre verticale '|', sans aucun espace. Les en-têtes de
réponse sont présentés de la même manière, mais après un espace suivant le bloc
d'en-tête de requête. Le tout précède la requête HTTP. Exemple :
Les en-têtes non trouvés sont logués à vide, et si un en-tête apparait
plusieurs fois, seule la dernière occurence sera conservée. Les en-têtes de
requête sont regroupés entre deux accolades '{' et '}' dans l'ordre de leur
déclaration, et chacun séparés par une barre verticale '|', sans aucun espace.
Les en-têtes de réponse sont présentés de la même manière, mais après un
espace suivant le bloc d'en-tête de requête. Le tout précède la requête HTTP.
Exemple :
Config:
@ -1350,8 +1406,8 @@ Aug 9 20:30:46 localhost haproxy[2022]: 127.0.0.1:34028 [09/Aug/2004:20:30:46]
----------------------------------
En mode HTTP uniquement, il est possible de remplacer certains en-têtes dans la
requête et/ou la réponse à partir d'expressions régulières. Il est également
possible de bloquer certaines requêtes en fonction du contenu des en-têtes ou de
la requête. Une limitation cependant : les en-têtes fournis au milieu de
possible de bloquer certaines requêtes en fonction du contenu des en-têtes ou
de la requête. Une limitation cependant : les en-têtes fournis au milieu de
connexions persistentes (keep-alive) ne sont pas vus car ils sont considérés
comme faisant partie des échanges de données consécutifs à la première requête.
Les données ne sont pas affectées, ceci ne s'applique qu'aux en-têtes.
@ -1517,14 +1573,14 @@ puisse corriger le nom du cookie dans toutes les futures requ
4.5) Protection contre les fuites d'informations du serveur
-----------------------------------------------------------
Dans les versions 1.1.28 et 1.2.1, une nouvelle option 'checkcache' a été créée.
Elle sert à inspecter minutieusement les entêtes 'Cache-control', 'Pragma', et
'Set-cookie' dans les réponses serveur pour déterminer s'il y a un risque de
cacher un cookie sur un proxy côté client. Quand cette option est activée, les
seules réponses qui peuvent être retournées au client sont :
Dans les versions 1.1.28 et 1.2.1, une nouvelle option 'checkcache' a été
créée. Elle sert à inspecter minutieusement les entêtes 'Cache-control',
'Pragma', et 'Set-cookie' dans les réponses serveur pour déterminer s'il y a
un risque de cacher un cookie sur un proxy côté client. Quand cette option est
activée, les seules réponses qui peuvent être retournées au client sont :
- toutes celles qui n'ont pas d'entête 'Set-cookie' ;
- toutes celles qui ont un code de retour autre que 200, 203, 206, 300, 301,
410, sauf si le server a positionné un entête 'Cache-control: public' ;
410, sauf si le serveur a positionné un entête 'Cache-control: public' ;
- celles qui font suite à une requête POST, sauf si le serveur a positionné
un entête 'Cache-control: public' ;
- celles qui ont un entête 'Pragma: no-cache' ;
@ -1557,7 +1613,7 @@ Certaines situations conduisent
Un message d'erreur succint tiré de la RFC accompagne ces codes de retour.
Cependant, en fonction du type de clientèle, on peut préférer retourner des
pages personnalisées. Ceci est possible par le biais de la commande "errorloc" :
pages personnalisées. Ceci est possible par le biais de la commande "errorloc":
errorloc <code_HTTP> <location>
@ -1595,9 +1651,9 @@ d
4.7) Changement des valeurs par défaut
--------------------------------------
Dans la version 1.1.22 est apparue la notion de valeurs par défaut, ce qui évite
de répéter des paramètres communs à toutes les instances, tels que les timeouts,
adresses de log, modes de fonctionnement, etc.
Dans la version 1.1.22 est apparue la notion de valeurs par défaut, ce qui
évite de répéter des paramètres communs à toutes les instances, tels que les
timeouts, adresses de log, modes de fonctionnement, etc.
Les valeurs par défaut sont positionnées dans la dernière section 'defaults'
précédent l'instance qui les utilisera. On peut donc mettre autant de sections

View File

@ -77,7 +77,7 @@
#include "include/appsession.h"
#define HAPROXY_VERSION "1.2.5"
#define HAPROXY_DATE "2005/04/24"
#define HAPROXY_DATE "2005/04/30"
/* this is for libc5 for example */
#ifndef TCP_NODELAY
@ -255,9 +255,9 @@ int strlcpy2(char *dst, const char *src, int size) {
#define sizeof_session sizeof(struct session)
#define sizeof_buffer sizeof(struct buffer)
#define sizeof_fdtab sizeof(struct fdtab)
#define sizeof_curappsession CAPTURE_LEN /* current_session pool */
#define sizeof_requri REQURI_LEN
#define sizeof_capture CAPTURE_LEN
#define sizeof_curappsession CAPTURE_LEN /* current_session pool */
#define sizeof_appsess sizeof(struct appsessions)
/* different possible states for the sockets */
@ -287,6 +287,11 @@ int strlcpy2(char *dst, const char *src, int size) {
#define POLL_LOOP_ACTION_RUN 1
#define POLL_LOOP_ACTION_CLEAN 2
/* poll mechanisms available */
#define POLL_USE_SELECT (1<<0)
#define POLL_USE_POLL (1<<1)
#define POLL_USE_EPOLL (1<<2)
/* bits for proxy->options */
#define PR_O_REDISP 0x00000001 /* allow reconnection to dispatch in case of errors */
#define PR_O_TRANSP 0x00000002 /* transparent mode : use original DEST as dispatch */
@ -622,8 +627,7 @@ static struct {
fd_set *StaticReadEvent,
*StaticWriteEvent;
int cfg_use_epoll = 0; /* use epoll() instead of select() ? */
int cfg_use_poll = 0; /* use poll() instead of select() ? */
int cfg_polling_mechanism = 0; /* POLL_USE_{SELECT|POLL|EPOLL} */
void **pool_session = NULL,
**pool_buffer = NULL,
@ -823,10 +827,10 @@ void usage(char *name) {
" -N sets the default, per-proxy maximum # of connections (%d)\n"
" -p writes pids of all children to this file\n"
#if defined(ENABLE_EPOLL)
" -E tries to use epoll() instead of select()\n"
" -de disables epoll() usage even when available\n"
#endif
#if defined(ENABLE_POLL)
" -P tries to use poll() instead of select()\n"
" -dp disables poll() usage even when available\n"
#endif
"\n",
name, DEFAULT_MAXCONN, cfg_maxpconn);
@ -5191,7 +5195,6 @@ int select_loop(int action) {
}
#endif
if (next_time > 0) { /* FIXME */
/* Convert to timeval */
/* to avoid eventual select loops due to timer precision */
@ -5247,10 +5250,10 @@ int select_loop(int action) {
*/
if (fdtab[fd].state == FD_STCLOSE)
continue;
if (FD_ISSET(fd, ReadEvent))
fdtab[fd].read(fd);
if (FD_ISSET(fd, WriteEvent))
fdtab[fd].write(fd);
}
@ -5462,6 +5465,7 @@ void dump(int sig) {
}
}
#ifdef DEBUG_MEMORY
static void fast_stop(void)
{
struct proxy *p;
@ -5494,6 +5498,7 @@ void sig_term(int sig) {
/* If we are killed twice, we decide to die*/
signal(sig, SIG_DFL);
}
#endif
/* returns the pointer to an error in the replacement string, or NULL if OK */
char *chain_regex(struct hdr_exp **head, regex_t *preg, int action, char *replace) {
@ -5535,6 +5540,12 @@ int cfg_parse_global(char *file, int linenum, char **args) {
else if (!strcmp(args[0], "debug")) {
global.mode |= MODE_DEBUG;
}
else if (!strcmp(args[0], "noepoll")) {
cfg_polling_mechanism &= ~POLL_USE_EPOLL;
}
else if (!strcmp(args[0], "nopoll")) {
cfg_polling_mechanism &= ~POLL_USE_POLL;
}
else if (!strcmp(args[0], "quiet")) {
global.mode |= MODE_QUIET;
}
@ -7139,6 +7150,14 @@ void init(int argc, char **argv) {
tmp++;
}
cfg_polling_mechanism = POLL_USE_SELECT; /* select() is always available */
#if defined(ENABLE_POLL)
cfg_polling_mechanism |= POLL_USE_POLL;
#endif
#if defined(ENABLE_EPOLL)
cfg_polling_mechanism |= POLL_USE_EPOLL;
#endif
pid = getpid();
progname = *argv;
while ((tmp = strchr(progname, '/')) != NULL)
@ -7157,12 +7176,12 @@ void init(int argc, char **argv) {
exit(0);
}
#if defined(ENABLE_EPOLL)
else if (*flag == 'E')
cfg_use_epoll = 1;
else if (*flag == 'd' && flag[1] == 'e')
cfg_polling_mechanism &= ~POLL_USE_EPOLL;
#endif
#if defined(ENABLE_POLL)
else if (*flag == 'P')
cfg_use_poll = 1;
else if (*flag == 'd' && flag[1] == 'p')
cfg_polling_mechanism &= ~POLL_USE_POLL;
#endif
else if (*flag == 'V')
arg_mode |= MODE_VERBOSE;
@ -7501,8 +7520,10 @@ int main(int argc, char **argv) {
signal(SIGQUIT, dump);
signal(SIGUSR1, sig_soft_stop);
signal(SIGHUP, sig_dump_state);
#ifdef DEBUG_MEMORY
signal(SIGINT, sig_int);
signal(SIGTERM, sig_term);
#endif
/* on very high loads, a sigpipe sometimes happen just between the
* getsockopt() which tells "it's OK to write", and the following write :-(
@ -7588,34 +7609,37 @@ int main(int argc, char **argv) {
}
#if defined(ENABLE_EPOLL)
if (cfg_use_epoll) {
if (cfg_polling_mechanism & POLL_USE_EPOLL) {
if (epoll_loop(POLL_LOOP_ACTION_INIT)) {
epoll_loop(POLL_LOOP_ACTION_RUN);
epoll_loop(POLL_LOOP_ACTION_CLEAN);
cfg_polling_mechanism &= POLL_USE_EPOLL;
}
else {
Warning("epoll() is not available. Trying poll() or select() instead.\n");
cfg_use_epoll = 0;
Warning("epoll() is not available. Using poll()/select() instead.\n");
cfg_polling_mechanism &= ~POLL_USE_EPOLL;
}
}
#endif
#if defined(ENABLE_POLL)
if (cfg_use_poll) {
if (cfg_polling_mechanism & POLL_USE_POLL) {
if (poll_loop(POLL_LOOP_ACTION_INIT)) {
poll_loop(POLL_LOOP_ACTION_RUN);
poll_loop(POLL_LOOP_ACTION_CLEAN);
cfg_polling_mechanism &= POLL_USE_POLL;
}
else {
Warning("poll() is not available. Using select() instead.\n");
cfg_use_poll = 0;
cfg_polling_mechanism &= ~POLL_USE_POLL;
}
}
#endif
if (!cfg_use_epoll && !cfg_use_poll) {
if (cfg_polling_mechanism & POLL_USE_SELECT) {
if (select_loop(POLL_LOOP_ACTION_INIT)) {
select_loop(POLL_LOOP_ACTION_RUN);
select_loop(POLL_LOOP_ACTION_CLEAN);
cfg_polling_mechanism &= POLL_USE_SELECT;
}
}