2 <!DOCTYPE modulesynopsis SYSTEM "../style/modulesynopsis.dtd">
3 <?xml-stylesheet type="text/xsl" href="../style/manual.fr.xsl"?>
4 <!-- English Revision : 981084 -->
5 <!-- French translation : Lucien GENTIS -->
6 <!-- Reviewed by : Vincent Deffontaines -->
9 Licensed to the Apache Software Foundation (ASF) under one or more
10 contributor license agreements. See the NOTICE file distributed with
11 this work for additional information regarding copyright ownership.
12 The ASF licenses this file to You under the Apache License, Version 2.0
13 (the "License"); you may not use this file except in compliance with
14 the License. You may obtain a copy of the License at
16 http://www.apache.org/licenses/LICENSE-2.0
18 Unless required by applicable law or agreed to in writing, software
19 distributed under the License is distributed on an "AS IS" BASIS,
20 WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
21 See the License for the specific language governing permissions and
22 limitations under the License.
25 <modulesynopsis metafile="mod_unique_id.xml.meta">
27 <name>mod_unique_id</name>
28 <description>Fournit une variable d'environnement contenant un
29 identifiant unique pour chaque requête</description>
30 <status>Extension</status>
31 <sourcefile>mod_unique_id.c</sourcefile>
32 <identifier>unique_id_module</identifier>
36 <p>Ce module fournit un identifiant dont l'unicité est garantie
37 parmi "toutes" les requêtes sous des conditions très précises.
38 L'identifiant unique le sera aussi parmi plusieurs machines
39 appartenant à un cluster correctement configuré. L'identifiant est
40 affecté à la variable d'environnement <code>UNIQUE_ID</code> pour
41 chaque requête. Les identifiants uniques sont utiles pour diverses
42 raisons dont la nature se situe au delà de la portée de ce
47 <title>Théorie</title>
49 <p>Tout d'abord un bref rappel de la manière dont le serveur Apache
50 fonctionne sous Unix (cette fonctionnalité n'étant actuellement pas
51 supportée sous Windows NT). Sous Unix, Apache crée plusieurs
52 processus enfants, ces derniers traitant les requêtes une par une.
53 Chaque processus enfant peut traiter plusieurs requêtes pendant sa
54 durée de vie. Dans le cadre de cette discussion, nous supposerons
55 que les différents processus enfants ne s'échangent pas de données
56 entre eux. Nous nous référerons aux processus enfants sous le nom de
57 <dfn>processus httpd</dfn>.</p>
59 <p>Votre site web est réparti entre une ou plusieurs machines dont
60 vous êtes l'administrateur, et que nous nommerons cluster de
61 serveurs. Chaque serveur peut exécuter plusieurs instances d'Apache.
62 L'ensemble de ces dernières sera considéré comme "l'Univers", et
63 sous certaines hypothèses, nous montrerons qu'il est possible dans
64 cet univers, de générer des identifiants uniques pour chaque
65 requête, sans pour autant nécessiter une communication importante
66 entre les différents serveurs du cluster.</p>
68 <p>Les machines de votre cluster doivent satisfaire ces conditions
69 (même si le cluster ne comporte qu'une machine, vous devez
70 synchroniser son horloge avec NTP) :</p>
73 <li>Les temps des machines sont synchronisés via NTP ou tout autre
74 protocole de synchronisation du temps en réseau.</li>
76 <li>Les nom d'hôtes des machines sont tous différents, de façon à
77 ce que le module puisse recevoir une adresse IP différente pour
78 chaque machine du cluster en effectuant une recherche sur le nom
82 <p>Au vu des caractéristiques actuelles du système d'exploitation,
83 nous supposerons que les pids (identifiants processus) sont codés
84 sur 32 bits. Si le système d'exploitation utilise plus de 32 bits
85 pour un pid, la correction est triviale mais doit être effectuée
88 <p>Ces hypothèses posées, à un instant donné, nous pouvons
89 distinguer tout processus httpd sur toute machine du cluster de tous
90 les autres processus httpd. Pour ce faire, il suffit d'utiliser
91 l'adresse IP de la machine et le pid du processus httpd. Un
92 processus httpd peut traiter plusieurs requêtes simultanément si
93 vous utilisez un module MPM multi-threadé. Pour identifier les
94 threads, Apache httpd utilise en interne un index de threads. Ainsi,
95 afin de générer des identifiants uniques pour chaque requête, il
96 suffit d'effectuer une distinction en fonction du temps.</p>
98 <p>Pour déterminer le temps, nous utiliserons un repère de temps
99 Unix (les secondes écoulées depuis le 1er janvier 1970 UTC), et un
100 compteur 16 bits. La précision du repère de temps n'étant que d'une
101 seconde, le compteur va représenter 65536 valeurs par seconde. Le
102 quadruplet <em>(adresse IP, pid, repère de temps, compteur)</em> est
103 en mesure de distinguer 65536 requêtes par seconde par processus
104 httpd. Il peut cependant arriver que le même pid soit réutilisé au
105 cours du temps, et le compteur est là pour pallier cet
106 inconvénient.</p>
108 <p>Lorsqu'un processus enfant httpd est créé, le compteur est
109 initialisé avec (nombre de microsecondes actuel divisé par 10)
110 modulo 65536 (cette formule a été choisie pour éliminer certains
111 problème de variance avec les bits de poids faibles du compteur de
112 microsecondes sur certains systèmes). Lorsqu'un identifiant unique
113 est généré, le repère de temps utilisé est le moment où la requête
114 arrive sur le serveur web. Le compteur est incrémenté à chaque
115 création d'identifiant (et peut repasser à 0 lorsqu'il a atteint sa
116 valeur maximale).</p>
118 <p>Le noyau génère un pid pour chaque processus lors de sa création,
119 et le compteur de pid est réinitialisé à une certaine valeur
120 lorsqu'il a atteint sa valeur maximale (les pid sont codés sur 16
121 bits sous de nombreux Unixes, mais les systèmes les plus récents les
122 ont étendus à 32 bits). La même valeur de pid pourra donc être
123 réutilisée au cours du temps. Cependant, tant qu'elle n'est pas
124 réutilisée dans la même seconde, elle ne remet pas en cause
125 l'unicité de notre quadruplet. Nous supposerons donc que le système
126 ne créera pas plus de 65536 processus en une seconde (ce nombre peut
127 être de 32768 sous certains Unixes, mais même dans ce cas, on est en
128 général loin de cette situation).</p>
130 <p>Il est possible que le temps se répète pour une raison
132 Supposons par exemple que l'horloge système soit retardée et repasse
133 par un temps passé (ou bien, comme elle avançait, elle a été remise
134 à l'heure, et elle repasse par un temps futur). Dans ce cas, il peut
135 être facilement démontré que le couple pid/repère de temps peut être
136 réutilisé. Le choix de la formule d'initialisation du compteur a
137 été effectué dans l'intention de pallier ce problème. Notez qu'un
138 nombre vraiment aléatoire serait souhaitable pour initialiser le
139 compteur, mais il n'existe pas de tel nombre directement lisible sur
140 la plupart des systèmes (c'est à dire que vous ne pouvez pas
141 utiliser rand() car vous devez déclencher le générateur avec une
142 valeur unique, et vous ne pouvez pas utiliser le temps à cet effet
143 car celui-ci , au moins à la seconde près, s'est répété). Il ne
144 s'agit donc pas d'une défense parfaite.</p>
146 <p>Même si elle n'est pas parfaite, quel est le degré d'efficacité
147 de cette défense ? Supposons
148 qu'une de vos machines serve au plus 500 requêtes par seconde (ce
149 qui constitue une limite supérieure très raisonnable au moment où ce
150 document est écrit, car les systèmes ne se contentent en général pas
151 de débiter des fichiers statiques). Pour y parvenir, un certain nombre
152 de processus enfants sera nécessaire, qui dépendra du nombre de
153 clients simultanés présents. Mais soyons pessimiste et supposons
154 qu'un seul processus enfant soit capable de servir 500 requêtes par
156 Il existe 1000 valeurs de démarrage possibles du compteur pour
157 lesquelles deux séquences de 500 requêtes puissent se recouvrir. Il
158 y a donc 1,5% de chance que le processus enfant répète une valeur de
159 compteur si le temps se répète (avec une résolution d'une seconde),
160 et l'unicité sera alors remise en cause. C'est cependant un exemple
161 très pessimiste, et avec les valeurs du monde réel, il y a bien
162 moins de chances que cela ne se produise. Si vous estimez que ceci a
163 tout de même quelque chances de se produire sur votre système, vous
164 pouvez migrer vers un compteur à 32 bits (en modifiant le code).</p>
166 <p>On pourrait supposer que ceci a plus de chance de se produire
167 lors du passage à l'heure d'hiver où l'horloge est "retardée". Cela
168 ne constitue cependant pas un problème car les temps pris en compte
169 ici sont des temps UTC, qui vont "toujours" de l'avant. Notez que
170 les Unixes à base de processeur x86 peuvent nécessiter une
171 configuration particulière pour que ceci soit vrai -- il doivent
172 être configurés pour assumer que l'horloge système est en UTC et
173 compenser de manière appropriée. Mais même dans ce cas, si vous
174 utilisez NTP, votre temps UTC sera correct peu après le
175 redémarrage.</p>
177 <!-- FIXME: thread_index is unsigned int, so not always 32bit.-->
178 <p>La variable d'environnement <code>UNIQUE_ID</code> est construite
179 par codage du quadruplet de 144 bits (adresse IP sur 32 bits, pid
180 sur 32 bits, repère de temps sur 32 bits, compteur 16 bits et index
181 de threads sur 32 bits) en
182 utilisant l'alphabet <code>[A-Za-z0-9@-]</code> d'une manière
183 similaire à celle du codage MIME base64, et sa valeur se présente
184 sous la forme d'une chaîne de 24 caractères. L'alphabet MIME base64
185 est en fait <code>[A-Za-z0-9+/]</code> ; cependant, les caractères
186 <code>+</code> et <code>/</code> nécessitent un codage particulier
187 dans les URLs, ce qui rend leur utilisation peu commode. Toutes les
188 valeurs sont codées dans l'ordre des octets d'une adresse réseau de
189 façon à ce
190 que le codage soit comparable entre des architectures où l'ordre des
191 octets est différent. L'ordre réel de codage est : repère de temps,
192 adresse IP, pid, compteur. Cet ordre de codage possède un but
193 précis, mais il faut souligner que les applications n'ont aucun
194 intérêt à entrer dans les détails de ce codage. Les applications
195 doivent se contenter de traiter la variable <code>UNIQUE_ID</code>
196 comme un symbole opaque, qui peut être comparé avec d'autres
197 <code>UNIQUE_ID</code>s en ne testant que leur égalité.</p>
199 <p>L'ordre a été choisi de façon à ce qu'il soit possible de
200 modifier le codage dans le futur sans avoir à se préoccuper de
201 conflits éventuels avec une base de données de
202 <code>UNIQUE_ID</code>s existante. Les nouveaux codages doivent
203 conserver le repère de temps comme premier élément, et pour le
204 reste, utiliser les même alphabet et longueur en bits. Comme les
205 repères de temps constituent essentiellement un séquence croissante,
206 il suffit que toutes les machines du cluster arrêtent de servir et
207 de requérir dans la même <em>seconde repère</em>, et n'utilisent
208 alors plus l'ancien format de codage. Ensuite, elles peuvent
209 reprendre le traitement des requêtes en utilisant les nouveaux
212 <p>Nous pensons que ceci apporte une solution relativement portable
213 au problème. Les
214 identifiants générés possèdent une durée de vie pratiquement infinie
215 car les identifiants futurs pourront être allongés selon les
216 besoins. Pratiquement aucune communication n'est requise entre les
217 machines du cluster (seule la synchronisation NTP est requise, ce
218 qui représente une charge très faible), et aucune communication
219 entre les processus httpd n'est nécessaire (la communication est
220 implicite et incluse dans le pid assigné par le noyau). Dans des
221 situations très spécifiques, l'identifiant peut être raccourci, mais
222 dans ce cas, d'avantage d'informations doivent être admises (par
223 exemple, les 32 bits de l'adresse IP sont excessifs pour la plupart
224 des sites, mais il n'existe pas de valeur de remplacement portable