<?xml version="1.0"?>
<!DOCTYPE modulesynopsis SYSTEM "/style/modulesynopsis.dtd">
<?xml-stylesheet type="text/xsl" href="/style/manual.fr.xsl"?>
<!-- English Revision : 1657407 -->
<!-- French translation : Lucien GENTIS -->
<!-- Reviewed by : Vincent Deffontaines -->
<!--
Licensed to the Apache Software Foundation (ASF) under one or more
contributor license agreements. See the NOTICE file distributed with
this work for additional information regarding copyright ownership.
The ASF licenses this file to You under the Apache License, Version 2.0
(the "License"); you may not use this file except in compliance with
the License. You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
-->
<modulesynopsis metafile="mod_unique_id.xml.meta">
<name>mod_unique_id</name>
<description>Fournit une variable d'environnement contenant un
identifiant unique pour chaque requ&ecirc;te</description>
<status>Extension</status>
<sourcefile>mod_unique_id.c</sourcefile>
<identifier>unique_id_module</identifier>
<summary>
<p>Ce module fournit un identifiant dont l'unicit&eacute; est garantie
parmi "toutes" les requ&ecirc;tes sous des conditions tr&egrave;s pr&eacute;cises.
L'identifiant unique le sera aussi parmi plusieurs machines
appartenant &agrave; un cluster correctement configur&eacute;. L'identifiant est
affect&eacute; &agrave; la variable d'environnement <code>UNIQUE_ID</code> pour
chaque requ&ecirc;te. Les identifiants uniques sont utiles pour diverses
raisons dont la nature se situe au del&agrave; de la port&eacute;e de ce
document.</p>
</summary>
<section id="theory">
<title>Th&eacute;orie</title>
<p>Tout d'abord un bref rappel de la mani&egrave;re dont le serveur Apache
fonctionne sous Unix (cette fonctionnalit&eacute; n'&eacute;tant actuellement pas
support&eacute;e sous Windows NT). Sous Unix, Apache cr&eacute;e plusieurs
processus enfants, ces derniers traitant les requ&ecirc;tes une par une.
Chaque processus enfant peut traiter plusieurs requ&ecirc;tes pendant sa
dur&eacute;e de vie. Dans le cadre de cette discussion, nous supposerons
que les diff&eacute;rents processus enfants ne s'&eacute;changent pas de donn&eacute;es
entre eux. Nous nous r&eacute;f&eacute;rerons aux processus enfants sous le nom de
<dfn>processus httpd</dfn>.</p>
<p>Votre site web est r&eacute;parti entre une ou plusieurs machines dont
vous &ecirc;tes l'administrateur, et que nous nommerons cluster de
serveurs. Chaque serveur peut ex&eacute;cuter plusieurs instances d'Apache.
L'ensemble de ces derni&egrave;res sera consid&eacute;r&eacute; comme "l'Univers", et
sous certaines hypoth&egrave;ses, nous montrerons qu'il est possible dans
cet univers, de g&eacute;n&eacute;rer des identifiants uniques pour chaque
requ&ecirc;te, sans pour autant n&eacute;cessiter une communication importante
entre les diff&eacute;rents serveurs du cluster.</p>
<p>Les machines de votre cluster doivent satisfaire ces conditions
(m&ecirc;me si le cluster ne comporte qu'une machine, vous devez
synchroniser son horloge avec NTP) :</p>
<ul>
<li>Les temps des machines sont synchronis&eacute;s via NTP ou tout autre
protocole de synchronisation du temps en r&eacute;seau.</li>
<li>Les nom d'h&ocirc;tes des machines sont tous diff&eacute;rents, de fa&ccedil;on &agrave;
ce que le module puisse recevoir une adresse IP diff&eacute;rente pour
chaque machine du cluster en effectuant une recherche sur le nom
d'h&ocirc;te.</li>
</ul>
<p>Au vu des caract&eacute;ristiques actuelles du syst&egrave;me d'exploitation,
nous supposerons que les pids (identifiants processus) sont cod&eacute;s
sur 32 bits. Si le syst&egrave;me d'exploitation utilise plus de 32 bits
pour un pid, la correction est triviale mais doit &ecirc;tre effectu&eacute;e
dans le code.</p>
<p>Ces hypoth&egrave;ses pos&eacute;es, &agrave; un instant donn&eacute;, nous pouvons
distinguer tout processus httpd sur toute machine du cluster de tous
les autres processus httpd. Pour ce faire, il suffit d'utiliser
l'adresse IP de la machine et le pid du processus httpd. Un
processus httpd peut traiter plusieurs requ&ecirc;tes simultan&eacute;ment si
vous utilisez un module MPM multi-thread&eacute;. Pour identifier les
threads, Apache httpd utilise en interne un index de threads. Ainsi,
afin de g&eacute;n&eacute;rer des identifiants uniques pour chaque requ&ecirc;te, il
suffit d'effectuer une distinction en fonction du temps.</p>
<p>Pour d&eacute;terminer le temps, nous utiliserons un rep&egrave;re de temps
Unix (les secondes &eacute;coul&eacute;es depuis le 1er janvier 1970 UTC), et un
compteur 16 bits. La pr&eacute;cision du rep&egrave;re de temps n'&eacute;tant que d'une
seconde, le compteur va repr&eacute;senter 65536 valeurs par seconde. Le
quadruplet <em>(adresse IP, pid, rep&egrave;re de temps, compteur)</em> est
en mesure de distinguer 65536 requ&ecirc;tes par seconde par processus
httpd. Il peut cependant arriver que le m&ecirc;me pid soit r&eacute;utilis&eacute; au
cours du temps, et le compteur est l&agrave; pour pallier cet
inconv&eacute;nient.</p>
<p>Lorsqu'un processus enfant httpd est cr&eacute;&eacute;, le compteur est
initialis&eacute; avec (nombre de microsecondes actuel divis&eacute; par 10)
modulo 65536 (cette formule a &eacute;t&eacute; choisie pour &eacute;liminer certains
probl&egrave;me de variance avec les bits de poids faibles du compteur de
microsecondes sur certains syst&egrave;mes). Lorsqu'un identifiant unique
est g&eacute;n&eacute;r&eacute;, le rep&egrave;re de temps utilis&eacute; est le moment o&ugrave; la requ&ecirc;te
arrive sur le serveur web. Le compteur est incr&eacute;ment&eacute; &agrave; chaque
cr&eacute;ation d'identifiant (et peut repasser &agrave; 0 lorsqu'il a atteint sa
valeur maximale).</p>
<p>Le noyau g&eacute;n&egrave;re un pid pour chaque processus lors de sa cr&eacute;ation,
et le compteur de pid est r&eacute;initialis&eacute; &agrave; une certaine valeur
lorsqu'il a atteint sa valeur maximale (les pid sont cod&eacute;s sur 16
bits sous de nombreux Unixes, mais les syst&egrave;mes les plus r&eacute;cents les
ont &eacute;tendus &agrave; 32 bits). La m&ecirc;me valeur de pid pourra donc &ecirc;tre
r&eacute;utilis&eacute;e au cours du temps. Cependant, tant qu'elle n'est pas
r&eacute;utilis&eacute;e dans la m&ecirc;me seconde, elle ne remet pas en cause
l'unicit&eacute; de notre quadruplet. Nous supposerons donc que le syst&egrave;me
ne cr&eacute;era pas plus de 65536 processus en une seconde (ce nombre peut
&ecirc;tre de 32768 sous certains Unixes, mais m&ecirc;me dans ce cas, on est en
g&eacute;n&eacute;ral loin de cette situation).</p>
<p>Il est possible que le temps se r&eacute;p&egrave;te pour une raison
quelconque.
Supposons par exemple que l'horloge syst&egrave;me soit retard&eacute;e et repasse
par un temps pass&eacute; (ou bien, comme elle avan&ccedil;ait, elle a &eacute;t&eacute; remise
&agrave; l'heure, et elle repasse par un temps futur). Dans ce cas, il peut
&ecirc;tre facilement d&eacute;montr&eacute; que le couple pid/rep&egrave;re de temps peut &ecirc;tre
r&eacute;utilis&eacute;. Le choix de la formule d'initialisation du compteur a
&eacute;t&eacute; effectu&eacute; dans l'intention de pallier ce probl&egrave;me. Notez qu'un
nombre vraiment al&eacute;atoire serait souhaitable pour initialiser le
compteur, mais il n'existe pas de tel nombre directement lisible sur
la plupart des syst&egrave;mes (c'est &agrave; dire que vous ne pouvez pas
utiliser rand() car vous devez d&eacute;clencher le g&eacute;n&eacute;rateur avec une
valeur unique, et vous ne pouvez pas utiliser le temps &agrave; cet effet
car celui-ci , au moins &agrave; la seconde pr&egrave;s, s'est r&eacute;p&eacute;t&eacute;). Il ne
s'agit donc pas d'une d&eacute;fense parfaite.</p>
<p>M&ecirc;me si elle n'est pas parfaite, quel est le degr&eacute; d'efficacit&eacute;
de cette d&eacute;fense ? Supposons
qu'une de vos machines serve au plus 500 requ&ecirc;tes par seconde (ce
qui constitue une limite sup&eacute;rieure tr&egrave;s raisonnable au moment o&ugrave; ce
document est &eacute;crit, car les syst&egrave;mes ne se contentent en g&eacute;n&eacute;ral pas
de d&eacute;biter des fichiers statiques). Pour y parvenir, un certain nombre
de processus enfants sera n&eacute;cessaire, qui d&eacute;pendra du nombre de
clients simultan&eacute;s pr&eacute;sents. Mais soyons pessimiste et supposons
qu'un seul processus enfant soit capable de servir 500 requ&ecirc;tes par
secondes.
Il existe 1000 valeurs de d&eacute;marrage possibles du compteur pour
lesquelles deux s&eacute;quences de 500 requ&ecirc;tes puissent se recouvrir. Il
y a donc 1,5% de chance que le processus enfant r&eacute;p&egrave;te une valeur de
compteur si le temps se r&eacute;p&egrave;te (avec une r&eacute;solution d'une seconde),
et l'unicit&eacute; sera alors remise en cause. C'est cependant un exemple
tr&egrave;s pessimiste, et avec les valeurs du monde r&eacute;el, il y a bien
moins de chances que cela ne se produise. Si vous estimez que ceci a
tout de m&ecirc;me quelque chances de se produire sur votre syst&egrave;me, vous
pouvez migrer vers un compteur &agrave; 32 bits (en modifiant le code).</p>
<p>On pourrait supposer que ceci a plus de chance de se produire
lors du passage &agrave; l'heure d'hiver o&ugrave; l'horloge est "retard&eacute;e". Cela
ne constitue cependant pas un probl&egrave;me car les temps pris en compte
ici sont des temps UTC, qui vont "toujours" de l'avant. Notez que
les Unixes &agrave; base de processeur x86 peuvent n&eacute;cessiter une
configuration particuli&egrave;re pour que ceci soit vrai -- il doivent
&ecirc;tre configur&eacute;s pour assumer que l'horloge syst&egrave;me est en UTC et
compenser de mani&egrave;re appropri&eacute;e. Mais m&ecirc;me dans ce cas, si vous
utilisez NTP, votre temps UTC sera correct peu apr&egrave;s le
red&eacute;marrage.</p>
<!-- FIXME: thread_index is unsigned int, so not always 32bit.-->
<p>La variable d'environnement <code>UNIQUE_ID</code> est construite
par codage du quadruplet de 144 bits (adresse IP sur 32 bits, pid
sur 32 bits, rep&egrave;re de temps sur 32 bits, compteur 16 bits et index
de threads sur 32 bits) en
utilisant l'alphabet <code>[A-Za-z0-9@-]</code> d'une mani&egrave;re
similaire &agrave; celle du codage MIME base64, et sa valeur se pr&eacute;sente
sous la forme d'une cha&icirc;ne de 24 caract&egrave;res. L'alphabet MIME base64
est en fait <code>[A-Za-z0-9+/]</code> ; cependant, les caract&egrave;res
<code>+</code> et <code>/</code> n&eacute;cessitent un codage particulier
dans les URLs, ce qui rend leur utilisation peu commode. Toutes les
valeurs sont cod&eacute;es dans l'ordre des octets d'une adresse r&eacute;seau de
fa&ccedil;on &agrave; ce
que le codage soit comparable entre des architectures o&ugrave; l'ordre des
octets est diff&eacute;rent. L'ordre r&eacute;el de codage est : rep&egrave;re de temps,
adresse IP, pid, compteur. Cet ordre de codage poss&egrave;de un but
pr&eacute;cis, mais il faut souligner que les applications n'ont aucun
int&eacute;r&ecirc;t &agrave; entrer dans les d&eacute;tails de ce codage. Les applications
doivent se contenter de traiter la variable <code>UNIQUE_ID</code>
comme un symbole opaque, qui peut &ecirc;tre compar&eacute; avec d'autres
<code>UNIQUE_ID</code>s en ne testant que leur &eacute;galit&eacute;.</p>
<p>L'ordre a &eacute;t&eacute; choisi de fa&ccedil;on &agrave; ce qu'il soit possible de
modifier le codage dans le futur sans avoir &agrave; se pr&eacute;occuper de
conflits &eacute;ventuels avec une base de donn&eacute;es de
<code>UNIQUE_ID</code>s existante. Les nouveaux codages doivent
conserver le rep&egrave;re de temps comme premier &eacute;l&eacute;ment, et pour le
reste, utiliser les m&ecirc;me alphabet et longueur en bits. Comme les
rep&egrave;res de temps constituent essentiellement un s&eacute;quence croissante,
il suffit que toutes les machines du cluster arr&ecirc;tent de traiter
toute requ&ecirc;te dans la m&ecirc;me <em>seconde rep&egrave;re</em>, et n'utilisent
alors plus l'ancien format de codage. Ensuite, elles peuvent
reprendre le traitement des requ&ecirc;tes en utilisant les nouveaux
codages.</p>
<p>Nous pensons que ceci apporte une solution relativement portable
au probl&egrave;me. Les
identifiants g&eacute;n&eacute;r&eacute;s poss&egrave;dent une dur&eacute;e de vie pratiquement infinie
car les identifiants futurs pourront &ecirc;tre allong&eacute;s selon les
besoins. Pratiquement aucune communication n'est requise entre les
machines du cluster (seule la synchronisation NTP est requise, ce
qui repr&eacute;sente une charge tr&egrave;s faible), et aucune communication
entre les processus httpd n'est n&eacute;cessaire (la communication est
implicite et incluse dans le pid assign&eacute; par le noyau). Dans des
situations tr&egrave;s sp&eacute;cifiques, l'identifiant peut &ecirc;tre raccourci, mais
dans ce cas, d'avantage d'informations doivent &ecirc;tre admises (par
exemple, les 32 bits de l'adresse IP sont excessifs pour la plupart
des sites, mais il n'existe pas de valeur de remplacement portable
plus courte).</p>
</section>
</modulesynopsis>