| 1 |
=============================== |
|---|
| 2 |
Le framework de cache de Django |
|---|
| 3 |
=============================== |
|---|
| 4 |
|
|---|
| 5 |
Un des aspects fondamental des sites web dynamiques est, évidemment, |
|---|
| 6 |
qu'ils sont dynamiques. A chaque fois qu'un utilisateur demande l'affichage |
|---|
| 7 |
d'une page, le serveur web effectue tout un ensemble de calculs -- allant |
|---|
| 8 |
des requêtes en base de données, jusqu'au rendu des gabarits, en passant |
|---|
| 9 |
par la logique métier -- afin de créer la page que vos visiteurs voient. |
|---|
| 10 |
Ceci est plus coûteux, en terme de surcharge de traitement, qu'une simple |
|---|
| 11 |
lecture de fichier sur le systÚme de fichiers de votre serveur. |
|---|
| 12 |
|
|---|
| 13 |
Pour la plupart des applications web, cette surcharge n'est pas un problÚme. |
|---|
| 14 |
La plupart des applications web ne sont pas le washingtonpost.com ou |
|---|
| 15 |
slashdot.org; ce sont simplement des sites de petites et moyennes taille |
|---|
| 16 |
avec un traffic de même ampleur. Mais pour les sites possédant un traffic |
|---|
| 17 |
moyen important, il est essentiel de réduire la surcharge autant que |
|---|
| 18 |
possible. |
|---|
| 19 |
|
|---|
| 20 |
C'est à ce moment que le cache entre en jeu. |
|---|
| 21 |
|
|---|
| 22 |
Mettre en cache un objet consiste à sauvegarder le résultat d'un calcul |
|---|
| 23 |
coûteux, de maniÚre à ne pas effectuer le traitement la prochaine fois. |
|---|
| 24 |
Voici un pseudocode détaillant comme cela fonctionne pour les pages web |
|---|
| 25 |
gérées dynamiquement:: |
|---|
| 26 |
|
|---|
| 27 |
pour une URL donnée, essayer de trouver la page dans le cache |
|---|
| 28 |
si la page est dans le cache: |
|---|
| 29 |
retourner la page cachée |
|---|
| 30 |
sinon: |
|---|
| 31 |
générer la page |
|---|
| 32 |
sauvegarder la page générée dans le cache (pour la prochaine fois) |
|---|
| 33 |
retourner la page générée |
|---|
| 34 |
|
|---|
| 35 |
Django fourni un systÚme de cache robuste qui vous permet de sauvegarder |
|---|
| 36 |
des pages dynamiques afin de ne pas avoir à recalculer chaque requête. |
|---|
| 37 |
Par commodité, Django offre une granularité de cache sur plusieurs niveaux : |
|---|
| 38 |
vous pouvez mettre en cache le résultat de sortie de vues spécifiques, |
|---|
| 39 |
vous pouvez ne mettre en cache que les parties difficiles à restituer, |
|---|
| 40 |
ou encore, vous pouvez mettre en cache votre site dans son entier. |
|---|
| 41 |
|
|---|
| 42 |
Django fonctionne aussi trÚs bien avec les caches situés en amont, tels |
|---|
| 43 |
que `Squid`_ et le cache des navigateurs. C'est le genre de cache que |
|---|
| 44 |
vous ne contrÎlez pas, mais sur lequel vous pouvez agir (via les entêtes |
|---|
| 45 |
HTTP) en indiquant quelles parties de votre site doivent être mises en |
|---|
| 46 |
cache, et comment. |
|---|
| 47 |
|
|---|
| 48 |
.. _Squid: http://www.squid-cache.org/ |
|---|
| 49 |
|
|---|
| 50 |
Configuration du cache |
|---|
| 51 |
======================= |
|---|
| 52 |
|
|---|
| 53 |
Le systÚme de cache ne nécessite qu'une petite partie de configuration. |
|---|
| 54 |
A savoir, vous n'avez besoin que de lui dire où les données seront stockées |
|---|
| 55 |
-- s'il s'agit de la base de données, du systÚme de fichiers, ou directement |
|---|
| 56 |
en mémoire. C'est une décision importante car elle affecte les performances |
|---|
| 57 |
de votre cache; oui, certains types de cache sont plus rapide que d'autres. |
|---|
| 58 |
|
|---|
| 59 |
Votre choix de cache est stocké dans le paramÚtre de configuration |
|---|
| 60 |
``CACHE_BACKEND`` de votre fichier de configuration. Ce qui suit détaille |
|---|
| 61 |
les différents valeurs permises pour CACHE_BACKEND. |
|---|
| 62 |
|
|---|
| 63 |
Memcached |
|---|
| 64 |
--------- |
|---|
| 65 |
|
|---|
| 66 |
De loin le plus rapide et le plus efficace des systÚmes de cache disponibles |
|---|
| 67 |
pour Django, Memcached est un framework de cache entiÚrement basé sur la |
|---|
| 68 |
mémoire, initialement développé pour gérer les forts taux de charge du |
|---|
| 69 |
LiveJournal.com et par la suite versé dans le libre par Danga Interactive. |
|---|
| 70 |
Il est utilisé par des sites tels que Slashdot et Wikipedia pour réduire |
|---|
| 71 |
les accÚs en base de données et améliorer les performances de maniÚre |
|---|
| 72 |
significative. |
|---|
| 73 |
|
|---|
| 74 |
Memcached est disponible gratuitement sur http://danga.com/memcached/ . |
|---|
| 75 |
Il s'exécute en tant que démon et il lui est alloué une certaine quantité |
|---|
| 76 |
de RAM. Tout ce qu'il fait est de fournir une interface -- une interface |
|---|
| 77 |
*super-rapide* -- pour ajouter, récupérer et supprimer des données |
|---|
| 78 |
arbitraires en cache. Toutes les données étant directement stockées en |
|---|
| 79 |
mémoire, il n'y a donc pas de surcharge d'utilisation de la base de |
|---|
| 80 |
données ou du systÚme de fichiers. |
|---|
| 81 |
|
|---|
| 82 |
AprÚs avoir installé Memcached, vous devrez installer la couche de liaison |
|---|
| 83 |
Python pour Memcached. Deux versions sont disponibles. Choisissez et |
|---|
| 84 |
installez *un* des modules suivants : |
|---|
| 85 |
|
|---|
| 86 |
* L'option la plus rapide est un module nommé ``cmemcache``, disponible |
|---|
| 87 |
sur http://gijsbert.org/cmemcache/ . (Ce module est uniquement |
|---|
| 88 |
compatible avec la version de développement de Django. Django 0.96 |
|---|
| 89 |
est uniquement compatible avec la seconde option qui suit.) |
|---|
| 90 |
|
|---|
| 91 |
* Si vous ne pouvez pas installer ``cmemcache``, vous pouvez installer |
|---|
| 92 |
``python-memcached``, disponible sur ftp://ftp.tummy.com/pub/python-memcached/ . |
|---|
| 93 |
Si cette URL est brisée allez simplement sur le site de Memcached |
|---|
| 94 |
(http://www.danga.com/memcached/) et récupérez la couche de liaison |
|---|
| 95 |
Python depuis la section "Client APIs". |
|---|
| 96 |
|
|---|
| 97 |
Pour utiliser Memcached avec Django, affectez la valeur ``memcached://ip:port/`` |
|---|
| 98 |
à ``CACHE_BACKEND``, dans laquelle ``ip`` désigne l'adresse IP de votre |
|---|
| 99 |
démon Memcached et ``port`` est le port sur lequel Memcached s'exécute. |
|---|
| 100 |
|
|---|
| 101 |
Dans cet exemple, Memcached s'exécute en local (127.0.0.1) sur le port 11211:: |
|---|
| 102 |
|
|---|
| 103 |
CACHE_BACKEND = 'memcached://127.0.0.1:11211/' |
|---|
| 104 |
|
|---|
| 105 |
Une des fonctionnalités phares de Memcached est sa capacité partager |
|---|
| 106 |
le cache à travers plusieurs serveurs. Pour utiliser cette fonctionnalité |
|---|
| 107 |
ajoutez toutes les adresses de vos serveurs dans ``CACHE_BACKEND``, séparées |
|---|
| 108 |
par un point virgule. Dans l'exemple qui suit, le cache est partagé à |
|---|
| 109 |
travers les instances Memcached s'exécutant aux adresses IP 172.19.26.240 |
|---|
| 110 |
et 172.19.26.242, toutes deux sur le port 11211:: |
|---|
| 111 |
|
|---|
| 112 |
CACHE_BACKEND = 'memcached://172.19.26.240:11211;172.19.26.242:11211/' |
|---|
| 113 |
|
|---|
| 114 |
Le cache basé sur la mémoire possÚde un inconvénient: du fait que les données |
|---|
| 115 |
sont gardées en mémoire, les données seront perdues si votre serveur tombe |
|---|
| 116 |
en panne. Clairement, la mémoire n'est pas destinée à la persistance de |
|---|
| 117 |
données. Ne dédiez donc pas uniquement votre gestion de cache sur la mémoire. |
|---|
| 118 |
En fait, aucun des systÚmes de cache de Django ne doit être utilisé comme |
|---|
| 119 |
moyen de stockage permanent des données -- ils sont tous destinés à fournir |
|---|
| 120 |
des solutions de cache, et non de stockage -- mais nous appuyons sur ce |
|---|
| 121 |
point du fait que le cache basé sur la mémoire est particuliÚrement |
|---|
| 122 |
temporaire. |
|---|
| 123 |
|
|---|
| 124 |
Cache via la base de données |
|---|
| 125 |
---------------------------- |
|---|
| 126 |
|
|---|
| 127 |
Pour utiliser une table de données comme systÚme de cache, premiÚrement, |
|---|
| 128 |
créez une table de cache dans votre base de données en exécutant la commande: |
|---|
| 129 |
|
|---|
| 130 |
python manage.py createcachetable [nom_table_de_cache] |
|---|
| 131 |
|
|---|
| 132 |
...dans laquelle ``[nom_table_de_cache]`` est le nom de la table à créer. |
|---|
| 133 |
(Ce nom peut être celui que vous voulez, tant qu'il s'agit d'un nom de |
|---|
| 134 |
table valide qui n'est pas déjà utilisé dans votre base de données.) Cette |
|---|
| 135 |
commande crée une unique table dans votre base de données dans le format |
|---|
| 136 |
propre utilisé par le systÚme de cache de Django. |
|---|
| 137 |
|
|---|
| 138 |
Une fois que vous avez créé cette table, affectez la valeur ``"db://nom_table"`` |
|---|
| 139 |
à ``CACHE_BACKEND``, où ``nom_table`` correspond au nom de votre table. |
|---|
| 140 |
Dans cet exemple, le nom de la table de cache est ``ma_table_de_cache``:: |
|---|
| 141 |
|
|---|
| 142 |
CACHE_BACKEND = 'db://ma_table_de_cache' |
|---|
| 143 |
|
|---|
| 144 |
Le cache en base de données fonctionne de maniÚre optimum lorsque vous |
|---|
| 145 |
possédez un serveur de base de données rapide et bien indéxée. |
|---|
| 146 |
|
|---|
| 147 |
Cache via le systÚme de fichiers |
|---|
| 148 |
-------------------------------- |
|---|
| 149 |
|
|---|
| 150 |
Pour sauvegarder les éléments du cache sur un systÚme de fichiers, utilisez |
|---|
| 151 |
le type de cache ``"file://"`` pour ``CACHE_BACKEND``. Par exemple, pour |
|---|
| 152 |
stocker les données en cache dans ``/var/tmp/cache_django``, utilisez |
|---|
| 153 |
cette configuration:: |
|---|
| 154 |
|
|---|
| 155 |
CACHE_BACKEND = 'file:///var/tmp/cache_django' |
|---|
| 156 |
|
|---|
| 157 |
Vous noterez qu'il y a trois barres obliques successives vers le début de |
|---|
| 158 |
cet exemple. Les deux premiÚres concernent ``file://``, et la troisiÚme est |
|---|
| 159 |
la premiÚre lettre du chemin de répertoire, ``/var/tmp/cache_django``. |
|---|
| 160 |
|
|---|
| 161 |
Le chemin de répertoire doit être de type absolu -- c'est-à -dire qu'il |
|---|
| 162 |
doit commencer à partir de l'élément racine de votre systÚme de fichiers. |
|---|
| 163 |
La barre oblique finale n'est pas obligatoire et ne revêt aucune |
|---|
| 164 |
importance particuliÚre. |
|---|
| 165 |
|
|---|
| 166 |
Assurez vous que le répertoire cible de la configuration existe bien et |
|---|
| 167 |
qu'il est accessible en lecture et écriture par l'utilisateur systÚme |
|---|
| 168 |
exécutant votre serveur web. Pour poursuivre l'exemple précédent, si votre |
|---|
| 169 |
serveur est exécuté par l'utilisateur ``apache``, assurez vous que le |
|---|
| 170 |
répertoire ``/var/tmp/cache_django`` existe et qu'il est accessible en |
|---|
| 171 |
lecture et écriture par l'utilisateur ``apache``. |
|---|
| 172 |
|
|---|
| 173 |
Cache via mémoire locale |
|---|
| 174 |
------------------------ |
|---|
| 175 |
|
|---|
| 176 |
Si vous voulez bénéficier des avantages de rapidité du cache en mémoire |
|---|
| 177 |
mais que vous ne pouvez pas exécuter Memcached, regardez du cÎté du systÚme |
|---|
| 178 |
de cache de mémoire locale. Ce cache est multi-processus et thread-safe |
|---|
| 179 |
(exécution des processus parallÚles sécurisée). Pour l'utiliser, affecter |
|---|
| 180 |
la valeur ``"locmem:///"`` Ã ``CACHE_BACKEND``. Par exemple:: |
|---|
| 181 |
|
|---|
| 182 |
CACHE_BACKEND = 'locmem:///' |
|---|
| 183 |
|
|---|
| 184 |
Cache basique (expérimental) |
|---|
| 185 |
---------------------------- |
|---|
| 186 |
|
|---|
| 187 |
Un mono-processus basique de cache mémoire disponible via ``"simple:///"``. |
|---|
| 188 |
Il sauvegarde simplement les données en cache du processus en cours, |
|---|
| 189 |
impliquant qu'il ne peut être utilisé que dans des environnements de |
|---|
| 190 |
développement ou de test. Par exemple :: |
|---|
| 191 |
|
|---|
| 192 |
CACHE_BACKEND = 'simple:///' |
|---|
| 193 |
|
|---|
| 194 |
**Nouveau dans la version de développement de Django:** Ce systÚme de cache est |
|---|
| 195 |
déprécié et sera supprimé dans une prochaine version. L'utilisation du systÚme |
|---|
| 196 |
de cache ``locmem`` est maintenant recommandé. |
|---|
| 197 |
|
|---|
| 198 |
Cache factice (expérimental) |
|---|
| 199 |
---------------------------- |
|---|
| 200 |
|
|---|
| 201 |
Enfin, Django s'accompagne d'un cache factice qui n'effectue aucun travail |
|---|
| 202 |
de cache -- il implémente simplement l'interface de cache sans rien faire |
|---|
| 203 |
d'autre. |
|---|
| 204 |
|
|---|
| 205 |
Ceci est utile si vous avez un site en production qui a de fort besoin en |
|---|
| 206 |
cache à plusieurs endroits différents mais un environnement de développement |
|---|
| 207 |
et/ou de test qui ne nécessite pas de cache. Ainsi, votre environnement de |
|---|
| 208 |
développement n'utilisera plus de cache, mais sans affecter celui de votre |
|---|
| 209 |
environnement de production. Pour activer le cache factice, assignez |
|---|
| 210 |
``CACHE_BACKEND`` comme suit:: |
|---|
| 211 |
|
|---|
| 212 |
CACHE_BACKEND = 'dummy:///' |
|---|
| 213 |
|
|---|
| 214 |
Arguments de CACHE_BACKEND |
|---|
| 215 |
-------------------------- |
|---|
| 216 |
|
|---|
| 217 |
Tous les caches peuvent recevoir des arguments. Ils sont passés sous forme |
|---|
| 218 |
de query-string au paramÚtre de configuration ``CACHE_BACKEND``. Les arguments |
|---|
| 219 |
possibles sont les suivants : |
|---|
| 220 |
|
|---|
| 221 |
timeout |
|---|
| 222 |
Temps d'expiration par défaut, en secondes, à utiliser par le cache. |
|---|
| 223 |
Défini par défaut à 5 minutes (300 secondes). |
|---|
| 224 |
|
|---|
| 225 |
max_entries |
|---|
| 226 |
Pour les caches bassique et via la base de données, le nombre maximum |
|---|
| 227 |
d'entrées permises en cache avant effacement. Par défaut à 300. |
|---|
| 228 |
|
|---|
| 229 |
cull_percentage |
|---|
| 230 |
Pourcentage d'entrées éliminées quand max_entries est atteint. Le |
|---|
| 231 |
véritable pourcentage est de 1/cull_percentage. De ce fait, |
|---|
| 232 |
l'affectation cull_percentage=3 entraînera la suppression d'1/3 |
|---|
| 233 |
des entrées lorsque max_entries est atteint. |
|---|
| 234 |
|
|---|
| 235 |
Une valeur égale à 0 pour cull_percentage signifie la destruction |
|---|
| 236 |
totale du cache lorsque max_entries est atteint. Ceci rend |
|---|
| 237 |
l'élimination beaucoup plus rapide aux dépens de plus d'absence |
|---|
| 238 |
dans l'antémémoire. |
|---|
| 239 |
|
|---|
| 240 |
Dans cet exemple, ``timeout`` Ã pour valeur ``60``:: |
|---|
| 241 |
|
|---|
| 242 |
CACHE_BACKEND = "memcached://127.0.0.1:11211/?timeout=60" |
|---|
| 243 |
|
|---|
| 244 |
Dans cet exemple, ``timeout`` vaut ``30`` et ``max_entries`` est à ``400``:: |
|---|
| 245 |
|
|---|
| 246 |
CACHE_BACKEND = "memcached://127.0.0.1:11211/?timeout=30&max_entries=400" |
|---|
| 247 |
|
|---|
| 248 |
Les arguments non valides sont ignorés silencieusement, tout comme les |
|---|
| 249 |
valeurs qui leurs sont associées. |
|---|
| 250 |
|
|---|
| 251 |
Le cache par site |
|---|
| 252 |
================= |
|---|
| 253 |
|
|---|
| 254 |
Une fois le cache configuré la maniÚre la plus simple de l'utiliser est |
|---|
| 255 |
encore de mettre en cache votre site dans son entier. Pour ce faire, |
|---|
| 256 |
ajoutez ``'django.middleware.cache.CacheMiddleware'`` à votre paramÚtre |
|---|
| 257 |
de configuration ``MIDDLEWARE_CLASSES``, comme dans l'exemple qui suit:: |
|---|
| 258 |
|
|---|
| 259 |
MIDDLEWARE_CLASSES = ( |
|---|
| 260 |
'django.middleware.cache.CacheMiddleware', |
|---|
| 261 |
'django.middleware.common.CommonMiddleware', |
|---|
| 262 |
) |
|---|
| 263 |
|
|---|
| 264 |
(L'ordre des ``MIDDLEWARE_CLASSES`` importe. Voir |
|---|
| 265 |
`Ordre des MIDDLEWARE_CLASSES`_ ci-dessous) |
|---|
| 266 |
|
|---|
| 267 |
Puis, ajoutez les paramÚtres de configuration obligatoires suivants dans |
|---|
| 268 |
votre fichier de configuration de Django: |
|---|
| 269 |
|
|---|
| 270 |
* ``CACHE_MIDDLEWARE_SECONDS`` -- Le nombre de secondes pendant lesquelles |
|---|
| 271 |
chaque page doit être cachée. |
|---|
| 272 |
* ``CACHE_MIDDLEWARE_KEY_PREFIX`` -- Si le cache est partagé à travers |
|---|
| 273 |
plusieurs sites utilisant la même installation de Django, affectez lui |
|---|
| 274 |
le nom du site, ou une autre chaîne de caractÚres unique pour cette |
|---|
| 275 |
instance de Django, afin de prévenir les interblocages de clés. Laissez |
|---|
| 276 |
vide si cela n'a pas d'importance pour vous. |
|---|
| 277 |
|
|---|
| 278 |
Le middleware de cache met en cache toute page ne contenant pas de paramÚtre |
|---|
| 279 |
passé via GET ou POST. De maniÚre optionnelle, si le paramÚtre |
|---|
| 280 |
``CACHE_MIDDLEWARE_ANONYMOUS_ONLY`` contient la valeur ``True``, seules |
|---|
| 281 |
les requêtes anonymes (i.e., celles qui ne sont pas exécutées par un |
|---|
| 282 |
utilisateur authentifié seront cachées. C'est une maniÚre simple et |
|---|
| 283 |
efficace de désactiver le cache pour les pages spécifiques aux utilisateurs |
|---|
| 284 |
(incluant les pages de l'interface d'administration de Django). Notez que |
|---|
| 285 |
si vous utilisez ``CACHE_MIDDLEWARE_ANONYMOUS_ONLY``, vous devriez vous |
|---|
| 286 |
assurez que vous avez bien activé ``AuthenticationMiddleware`` et que |
|---|
| 287 |
``AuthenticationMiddleware`` soit placé avant ``CacheMiddleware`` dans |
|---|
| 288 |
vos ``MIDDLEWARE_CLASSES``. |
|---|
| 289 |
|
|---|
| 290 |
De plus, ``CacheMiddleware`` ajoute automatiquement quelques entrées dans |
|---|
| 291 |
chaque ``HttpResponse``: |
|---|
| 292 |
|
|---|
| 293 |
* Assigne à l'entête ``Last-Modified`` la date et l'heure courante quand |
|---|
| 294 |
une nouvelle version (non caché) de la page est demandée. |
|---|
| 295 |
* Assigne à l'entête ``Expires`` la date et l'heure courante augmentée du |
|---|
| 296 |
nombre de secondes définies par ``CACHE_MIDDLEWARE_SECONDS``. |
|---|
| 297 |
* Assigne à l'entête ``Cache-Control`` la durée de vie maximum de la page |
|---|
| 298 |
-- de même, basée sur le paramÚtre de configuration ``CACHE_MIDDLEWARE_SECONDS`` |
|---|
| 299 |
|
|---|
| 300 |
Voir la `documentation sur les middlewares`_ pour en savoir plus sur les |
|---|
| 301 |
middleware. |
|---|
| 302 |
|
|---|
| 303 |
.. _`documentation sur les middlewares`: ../middleware/ |
|---|
| 304 |
|
|---|
| 305 |
**Nouveau dans la version de développement de Django** |
|---|
| 306 |
|
|---|
| 307 |
Si une vue gÚre son propre temps d'expiration de cache (i.e. possÚde une section |
|---|
| 308 |
``max-age`` dans son entête ``CacheControl``) alors la page sera mise en cache |
|---|
| 309 |
jusqu'Ã expiration du temps, au lieu de ``CACHE_MIDDLEWARE_SECONDS``. |
|---|
| 310 |
L'utilisation des décorateurs de ``django.views.decorators.cache`` facilite |
|---|
| 311 |
l'assignation du temps d'expiration pour une vue (via le décorateur |
|---|
| 312 |
``cache_control``) ou la désactivation du cache pour une vue (via le décorateur |
|---|
| 313 |
``never_cache``). Voir la section `Utilisation d'autres entêtes`__ pour plus de |
|---|
| 314 |
décorateurs. |
|---|
| 315 |
|
|---|
| 316 |
__ `ContrÎle du cache: Utilisation d'autres entêtes`_ |
|---|
| 317 |
|
|---|
| 318 |
Le cache par vue |
|---|
| 319 |
================ |
|---|
| 320 |
|
|---|
| 321 |
Une maniÚre plus granulaire d'utiliser le framework de cache est de mettre |
|---|
| 322 |
en cache le résultat de sortie de chaque vue. Le ``django.views.decorators.cache`` |
|---|
| 323 |
défini un décorateur ``cache_page`` qui mettra automatiquement en cache |
|---|
| 324 |
la réponse de la vue pour vous. C'est trÚs facile à utiliser:: |
|---|
| 325 |
|
|---|
| 326 |
from django.views.decorators.cache import cache_page |
|---|
| 327 |
|
|---|
| 328 |
def slashdot_cela(request): |
|---|
| 329 |
... |
|---|
| 330 |
|
|---|
| 331 |
slashdot_cela = cache_page(slashdot_cela, 60 * 15) |
|---|
| 332 |
|
|---|
| 333 |
Ou, en utilisant la syntaxe des décorateurs de Python 2.4:: |
|---|
| 334 |
|
|---|
| 335 |
@cache_page(60 * 15) |
|---|
| 336 |
def slashdot_cela(request): |
|---|
| 337 |
... |
|---|
| 338 |
|
|---|
| 339 |
``cache_page`` ne prends qu'un seul argument : le temps d'expiration du |
|---|
| 340 |
cache, en secondes. Dans l'exemple ci-dessus, le résultat de la vue |
|---|
| 341 |
``slashdot_cela()`` sera mis en cache pendant 15 minutes. |
|---|
| 342 |
|
|---|
| 343 |
Cache des gabarits par fragments |
|---|
| 344 |
================================ |
|---|
| 345 |
|
|---|
| 346 |
**Nouveau dans la version de développement de Django** |
|---|
| 347 |
|
|---|
| 348 |
Si vous avez besoin de plus de contrÃŽle, vous pouvez aussi mettre en cache des |
|---|
| 349 |
fragments de gabarits en utilisant le tag de gabarit ``cache``. Pour que votre |
|---|
| 350 |
gabarit puisse accéder à ce tag, ajoutez ``{% load cache %}`` vers le début de |
|---|
| 351 |
votre gabarit. |
|---|
| 352 |
|
|---|
| 353 |
Le tag de gabarit ``{% cache %}`` met en cache le contenu du bloc pour la |
|---|
| 354 |
période de temps donnée. Il prend au moins deux arguments : le temps maximum de |
|---|
| 355 |
mise en cache, en secondes, et le nom du fragment de cache. Par exemple:: |
|---|
| 356 |
|
|---|
| 357 |
{% load cache %} |
|---|
| 358 |
{% cache 500 barre_laterale %} |
|---|
| 359 |
.. barre_laterale .. |
|---|
| 360 |
{% endcache %} |
|---|
| 361 |
|
|---|
| 362 |
Certaines fois, vous aurez besoin de mettre en cache plusieurs copies d'un |
|---|
| 363 |
fragment dépendant de données dynamiques apparaissant au sein même du fragment. |
|---|
| 364 |
Par exemple, vous voudriez une copie séparée de la barre latérale utilisée dans |
|---|
| 365 |
l'exemple précédent pour chaque utilisateur de votre site. Pour ce faire, |
|---|
| 366 |
passez un argument supplémentaire au tag de gabarit ``{% cache %}`` pour |
|---|
| 367 |
identifier le fragment de maniÚre unique:: |
|---|
| 368 |
|
|---|
| 369 |
{% load cache %} |
|---|
| 370 |
{% cache 500 barre_laterale request.user.username %} |
|---|
| 371 |
.. barre_laterale pour l'utilisateur connecté .. |
|---|
| 372 |
{% endcache %} |
|---|
| 373 |
|
|---|
| 374 |
Il est tout a fait possible de spécifier plus d'un argument pour identifier le |
|---|
| 375 |
fragment. Passez autant d'arguments à ``{% cache %}`` que nécessaire. |
|---|
| 376 |
|
|---|
| 377 |
L'API de cache de bas niveau |
|---|
| 378 |
============================ |
|---|
| 379 |
|
|---|
| 380 |
Des fois, cependant, mettre en cache l'intégralité d'une page ne vous |
|---|
| 381 |
intéresse pas forcément. Par exemple, vous pouviez trouver qu'il n'est |
|---|
| 382 |
nécessaire que de mettre en cache le résultat d'une requête en base |
|---|
| 383 |
de données coûteuse. Dans ce genre de cas, vous pouvez utiliser l'API de |
|---|
| 384 |
cache de bas niveau pour mettre en cache des objets avec le niveau de |
|---|
| 385 |
granularité de votre choix. |
|---|
| 386 |
|
|---|
| 387 |
L'API de cache est simple. Le module de cache, ``django.core.cache``, met |
|---|
| 388 |
à disposition un objet ``cache`` automatiquement créé selon le paramÚtre |
|---|
| 389 |
de configuration ``CACHE_BACKEND``:: |
|---|
| 390 |
|
|---|
| 391 |
>>> from django.core.cache import cache |
|---|
| 392 |
|
|---|
| 393 |
L'interface de base est ``set(clé, valeur, temps_expiration_en_secondes)`` |
|---|
| 394 |
et ``get(clé)``:: |
|---|
| 395 |
|
|---|
| 396 |
>>> cache.set('ma_clé, 'bonjour, monde!', 30) |
|---|
| 397 |
>>> cache.get('my_clé) |
|---|
| 398 |
'bonjour, monde!' |
|---|
| 399 |
|
|---|
| 400 |
L'argument ``temps_expiration_en_secondes`` est optionnel et est par défaut |
|---|
| 401 |
égal à la valeur définie dans le paramÚtre de configuration ``CACHE_BACKEND`` |
|---|
| 402 |
(expliqué plus haut). |
|---|
| 403 |
|
|---|
| 404 |
Si l'objet n'existe pas en cache, ``cache.get()`` retourne ``None``:: |
|---|
| 405 |
|
|---|
| 406 |
>>> cache.get('une_autre_clé') |
|---|
| 407 |
None |
|---|
| 408 |
|
|---|
| 409 |
# Attendre 30 secondes que 'ma_clé' expire... |
|---|
| 410 |
|
|---|
| 411 |
>>> cache.get('ma_clé) |
|---|
| 412 |
None |
|---|
| 413 |
|
|---|
| 414 |
get() peut prendre un argument ``default``:: |
|---|
| 415 |
|
|---|
| 416 |
>>> cache.get('ma_clé', 'est expiré') |
|---|
| 417 |
'est expiré' |
|---|
| 418 |
|
|---|
| 419 |
**Nouveau dans la version de développement de Django** Pour ajouter une clé |
|---|
| 420 |
uniquement si elle n'existe pas déjà , utilisez la méthode ``add()``. Elle prend |
|---|
| 421 |
les mêmes paramÚtres que ``set()``, sauf qu'elle n'essayera pas de mettre à jour |
|---|
| 422 |
le cache si la clé spécifiée est déjà présente:: |
|---|
| 423 |
|
|---|
| 424 |
>>> cache.set('ajout_clé', 'Valeur initiale') |
|---|
| 425 |
>>> cache.add('ajout_clé', 'Nouvelle valeur') |
|---|
| 426 |
>>> cache.get('ajout_clé') |
|---|
| 427 |
'Valeur initiale' |
|---|
| 428 |
|
|---|
| 429 |
Il y aussi une méthode get_many() qui n'attaque le cache qu'une seule fois. |
|---|
| 430 |
get_many() retourne un dictionnaire avec toutes les clés que vous avez |
|---|
| 431 |
demandé pour celles qui existent encore en cache (et n'ont pas expiré): |
|---|
| 432 |
|
|---|
| 433 |
>>> cache.set('a', 1) |
|---|
| 434 |
>>> cache.set('b', 2) |
|---|
| 435 |
>>> cache.set('c', 3) |
|---|
| 436 |
>>> cache.get_many(['a', 'b', 'c']) |
|---|
| 437 |
{'a': 1, 'b': 2, 'c': 3} |
|---|
| 438 |
|
|---|
| 439 |
Finalement, vous pouvez directement supprimer des clés avec ``delete()``. |
|---|
| 440 |
C'est une maniÚre facile de supprimer un objet particulier du cache:: |
|---|
| 441 |
|
|---|
| 442 |
>>> cache.delete('a') |
|---|
| 443 |
|
|---|
| 444 |
C'est tout. Le cache possÚde quelques restrictions : Vous pouvez mettre |
|---|
| 445 |
en cache tout objet qui se conserve de maniÚre sécurisée tandis que les |
|---|
| 446 |
clés doivent être des chaînes de caractÚres. |
|---|
| 447 |
|
|---|
| 448 |
Les caches en amont |
|---|
| 449 |
=================== |
|---|
| 450 |
|
|---|
| 451 |
Jusqu'ici, ce document s'en est tenu à la mise en cache de *vos* données. |
|---|
| 452 |
Mais un autre type de cache est aussi pertinent pour le développement web: |
|---|
| 453 |
le cache géré par les serveurs de cache en "amont". Ce sont des systÚmes |
|---|
| 454 |
qui mettent en cache les pages pour les utilisateurs avant même que la |
|---|
| 455 |
requête n'atteigne votre site web. |
|---|
| 456 |
|
|---|
| 457 |
Voici quelques exemples de caches en amont: |
|---|
| 458 |
|
|---|
| 459 |
* Votre FAI peut mettre en cache certaines pages. Ainsi, si vous |
|---|
| 460 |
requêtez le page de undomaine.com, votre FAI vous enverra la page |
|---|
| 461 |
sans accéder directement à undomaine.com. |
|---|
| 462 |
|
|---|
| 463 |
* Votre site Django est derriÚre un proxy web `Squid`_ qui met en cache |
|---|
| 464 |
les pages pour améliorer les performances. Dans ce genre de cas, |
|---|
| 465 |
chaque requête est d'abord gérée ar Squid, et ne la repassera à |
|---|
| 466 |
votre application que si besoin est. |
|---|
| 467 |
|
|---|
| 468 |
* Votre navigateur web met aussi en cache les pages. Si une page web |
|---|
| 469 |
envoi les bons entêtes, votre navigateur utilisera la copie locale |
|---|
| 470 |
(en cache) pour les requêtes suivantes, sur cette page. |
|---|
| 471 |
|
|---|
| 472 |
Le cache exercé en amont amÚne un bon boost de performance, mais il y a |
|---|
| 473 |
un danger à son utilisation: Le contenu des pages web diffÚrent en regard |
|---|
| 474 |
de l'authentification, de l'hÎte ou d'autres variables, et les systÚmes |
|---|
| 475 |
de cache qui sauvegarde les pages en aveugle en se basant seulement sur |
|---|
| 476 |
l'URL peuvent exposer des données incorrectes ou sensibles aux utilisateurs |
|---|
| 477 |
qui suivent et demandent ces même pages. |
|---|
| 478 |
|
|---|
| 479 |
Par exemple, disons que vous faîtes fonctionner une application web de |
|---|
| 480 |
gestion de courriels, et que le contenu de la page "boîte de réception" |
|---|
| 481 |
dépend de l'utilisateur qui est connecté. Si un FAI met aveuglément en |
|---|
| 482 |
cache votre site, alors le premier utilisateur qui se connectera via cet |
|---|
| 483 |
FAI verra apparaître sa page contenant ses données personnelles sur celle |
|---|
| 484 |
des utilisateurs qui suivent. Ce qui n'est pas top. |
|---|
| 485 |
|
|---|
| 486 |
Heureusement, HTTP apporte une solution à ce problÚme: Un ensemble d'entêtes |
|---|
| 487 |
HTTP existe pour instruire les mécanismes de cache et différencier le |
|---|
| 488 |
contenu de cache en fonction de variables données, et d'ordonner aux |
|---|
| 489 |
mécanismes de cache de ne pas mettre en cache certaines pages. |
|---|
| 490 |
|
|---|
| 491 |
.. _`Squid`: http://www.squid-cache.org/ |
|---|
| 492 |
|
|---|
| 493 |
Utilisation des entêtes Vary |
|---|
| 494 |
============================ |
|---|
| 495 |
|
|---|
| 496 |
Un de ces entêtes est Vary. Il défini quelles sont les entêtes de la |
|---|
| 497 |
requête que le mécanisme de cache doit prendre en compte lors de la |
|---|
| 498 |
construction de sa clé de cache. Par exemple, si le contenu d'une page |
|---|
| 499 |
web dépend de la langue préférée de l'utilisateur, la page est dite à |
|---|
| 500 |
"varier en fonction de la langue". |
|---|
| 501 |
|
|---|
| 502 |
Par défaut, le systÚme de cache de Django crée sa clé de cache en utilisant |
|---|
| 503 |
le chemin requêté -- e.g., ``"/nouvelles/2005/juin/23/vol_de_banque/"``. |
|---|
| 504 |
Ceci veut dire que chaque requête sur cette URL utilisera la même version |
|---|
| 505 |
en cache, sans se soucier des différences émanant des agents utilisateurs |
|---|
| 506 |
tels que les cookies ou la langue. |
|---|
| 507 |
|
|---|
| 508 |
C'est à ce moment que ``Vary`` entre en jeu. |
|---|
| 509 |
|
|---|
| 510 |
Si votre page Django génÚre un contenu différent en fonction des entêtes |
|---|
| 511 |
des requêtes -- tels les cookies, la langue ou l'agent utilisateur -- vous |
|---|
| 512 |
aurez besoin de l'entête ``Vary`` pour spécifier au mécanisme de cache |
|---|
| 513 |
que le contenu de votre page dépend de ce genre de chose. |
|---|
| 514 |
|
|---|
| 515 |
Pour faire cela avec Django, utilisez le décorateur de vue taillé sur |
|---|
| 516 |
mesure ``vary_on_headers``, de cette maniÚre:: |
|---|
| 517 |
|
|---|
| 518 |
from django.views.decorators.vary import vary_on_headers |
|---|
| 519 |
|
|---|
| 520 |
# Syntaxe Python 2.3. |
|---|
| 521 |
def ma_vue(request): |
|---|
| 522 |
... |
|---|
| 523 |
ma_vue = vary_on_headers(ma_vue, 'User-Agent') |
|---|
| 524 |
|
|---|
| 525 |
# Syntaxe décorateur de Python 2.4. |
|---|
| 526 |
@vary_on_headers('User-Agent') |
|---|
| 527 |
def ma_vue(request): |
|---|
| 528 |
... |
|---|
| 529 |
|
|---|
| 530 |
Ici, un mécanisme de cache (tel que le middleware de cache propre à Django) |
|---|
| 531 |
mettra en cache une version séparée de la page pour chaque agent utilisateur |
|---|
| 532 |
unique. |
|---|
| 533 |
|
|---|
| 534 |
L'avantage d'utiliser le décorateur ``vary_on_headers`` au lieu d'effectuer |
|---|
| 535 |
manuellement le paramétrage de l'entête ``Vary`` (en utilisant quelque |
|---|
| 536 |
chose comme ``response['Vary'] = 'user-agent'``) est que le décorateur |
|---|
| 537 |
ajoute la donnée d'entête ``Vary`` (qui existe peut être déjà ) au lieu |
|---|
| 538 |
de simplement la redéfinir. |
|---|
| 539 |
|
|---|
| 540 |
Vous pouvez ajouter plusieurs entêtes à ``vary_on_headers()``:: |
|---|
| 541 |
|
|---|
| 542 |
@vary_on_headers('User-Agent', 'Cookie') |
|---|
| 543 |
def ma_vue(request): |
|---|
| 544 |
... |
|---|
| 545 |
|
|---|
| 546 |
Du fait que la dépendance via un cookie est un cas commun, il existe un |
|---|
| 547 |
décorateur ``vary_on_cookie``. Ces deux vues sont équivalentes:: |
|---|
| 548 |
|
|---|
| 549 |
@vary_on_cookie |
|---|
| 550 |
def ma_vue(request): |
|---|
| 551 |
... |
|---|
| 552 |
|
|---|
| 553 |
@vary_on_headers('Cookie') |
|---|
| 554 |
def ma_vue(request): |
|---|
| 555 |
... |
|---|
| 556 |
|
|---|
| 557 |
Vous noterez aussi que les entêtes que vous passez à ``vary_on_headers`` |
|---|
| 558 |
ne sont pas sensibles à la casse. ``"User-Agent"`` est identique à ``"user-agent"``. |
|---|
| 559 |
|
|---|
| 560 |
Vous pouvez aussi directement utiliser l'assistant ``django.utils.cache.patch_vary_headers``:: |
|---|
| 561 |
|
|---|
| 562 |
from django.utils.cache import patch_vary_headers |
|---|
| 563 |
def ma_vue(request): |
|---|
| 564 |
... |
|---|
| 565 |
reponse = render_to_response('nom_gabarit', context) |
|---|
| 566 |
patch_vary_headers(reponse, ['Cookie']) |
|---|
| 567 |
return reponse |
|---|
| 568 |
|
|---|
| 569 |
``patch_vary_headers`` prends une instance ``HttpResponse`` en tant que |
|---|
| 570 |
premier argument et une liste ou un tuple de noms d'entêtes en second |
|---|
| 571 |
argument. |
|---|
| 572 |
|
|---|
| 573 |
Pour en savoir plus sur les headers Vary, regardez la `spécification |
|---|
| 574 |
officielle de vary`_. |
|---|
| 575 |
|
|---|
| 576 |
.. _`spécification officielle de vary`: http://www.w3.org/Protocols/rfc2616/rfc2616-sec14.html#sec14.44 |
|---|
| 577 |
|
|---|
| 578 |
ContrÎle du cache: Utilisation d'autres entêtes |
|---|
| 579 |
=============================================== |
|---|
| 580 |
|
|---|
| 581 |
Un autre problÚme avec le cache est la protection de vos données et la |
|---|
| 582 |
question de savoir où vos données doivent être stockées parmi la cascade |
|---|
| 583 |
de systÚmes de cache. |
|---|
| 584 |
|
|---|
| 585 |
Un utilisateur est généralement en proie à deux types de cache: son propre |
|---|
| 586 |
cache de navigateur (un cache privé) et le cache de son fournisseur d'accÚs |
|---|
| 587 |
(un cache public). Un cache public est utilisé par de multiple utilisateurs |
|---|
| 588 |
et contrÎlé par d'autres. Ceci pose problÚme pour les données sensibles: |
|---|
| 589 |
Vous ne voulez pas, par exemple, que votre compte bancaire soit stocké |
|---|
| 590 |
dans un cache public. |
|---|
| 591 |
|
|---|
| 592 |
La solution est d'indiquer que la page en cache doit être "privée". Pour |
|---|
| 593 |
faire cela dans Django, utilisez le décorateur de vue ``cache_control``. |
|---|
| 594 |
Exemple:: |
|---|
| 595 |
|
|---|
| 596 |
from django.views.decorators.cache import cache_control |
|---|
| 597 |
@cache_control(private=True) |
|---|
| 598 |
def ma_vue(request): |
|---|
| 599 |
... |
|---|
| 600 |
|
|---|
| 601 |
Ce décorateur prendra soin d'envoyer l'entête HTTP appropriée de maniÚre |
|---|
| 602 |
transparente. |
|---|
| 603 |
|
|---|
| 604 |
Il y a d'autres maniÚres de contrÎler les paramÚtres de cache. Par exemple, |
|---|
| 605 |
HTTP permet aux applications de faire ce qui suit: |
|---|
| 606 |
|
|---|
| 607 |
* Définir le temps maximum pendant lequel une page est en cache. |
|---|
| 608 |
* Déterminer si le cache doit systématiquement vérifier qu'une nouvelle |
|---|
| 609 |
version existe, ne délivrant le contenu en cache seulement s'il |
|---|
| 610 |
n'y a pas eu de changements. (Certains systÚmes de cache peuvent |
|---|
| 611 |
délivrer du contenu en cache même si la page cÎté serveur a changé |
|---|
| 612 |
-- tout simplement parce qu'une copie existe en cache et qu'elle |
|---|
| 613 |
n'est pas expirée.) |
|---|
| 614 |
|
|---|
| 615 |
Dans Django, utilisez le décorateur de vue ``cache_control`` pour spécifier |
|---|
| 616 |
ce genre de paramÚtres de cache. Dans l'exemple qui suit, ``cache_control`` |
|---|
| 617 |
indique aux systÚmes de caches de revalider leur cache à chaque accés et |
|---|
| 618 |
de ne garder en cache les éléments que pendant, au plus, 3600 secondes:: |
|---|
| 619 |
|
|---|
| 620 |
from django.views.decorators.cache import cache_control |
|---|
| 621 |
@cache_control(must_revalidate=True, max_age=3600) |
|---|
| 622 |
def ma_vue(request): |
|---|
| 623 |
... |
|---|
| 624 |
|
|---|
| 625 |
Toutes les directives ``Cache-Control`` valides pour HTTP le sont aussi |
|---|
| 626 |
pour ``cache_control()``. Voici la liste complÚte: |
|---|
| 627 |
|
|---|
| 628 |
* ``public=True`` |
|---|
| 629 |
* ``private=True`` |
|---|
| 630 |
* ``no_cache=True`` |
|---|
| 631 |
* ``no_transform=True`` |
|---|
| 632 |
* ``must_revalidate=True`` |
|---|
| 633 |
* ``proxy_revalidate=True`` |
|---|
| 634 |
* ``max_age=nombre_secondes`` |
|---|
| 635 |
* ``s_maxage=nombre_secondes`` |
|---|
| 636 |
|
|---|
| 637 |
Pour des plus amples détails sur les directives Cache-Control de HTTP, |
|---|
| 638 |
regardez la `spécification de Cache-Control`_. |
|---|
| 639 |
|
|---|
| 640 |
(Notez que le middleware de cache assigne déjà le max-age de l'entête via |
|---|
| 641 |
la valeur du paramÚtre de configuration ``CACHE_MIDDLEWARE_SETTINGS``. Si |
|---|
| 642 |
vous utilisez une autre valeur de ``max_age`` dans le décorateur |
|---|
| 643 |
``cache_control``, ce dernier obtiendra la précédente, et la valeur de |
|---|
| 644 |
l'entité sera correctement assignée.) |
|---|
| 645 |
|
|---|
| 646 |
Si vous désirez utiliser les entêtes pour désactiver le cache dans son entier, |
|---|
| 647 |
le décorateur de vue ``django.views.decorators.cache.never_cache`` ajoute des entêtes |
|---|
| 648 |
pour s'assurer que la réponse ne sera pas mise en cache par les navigateurs ou |
|---|
| 649 |
d'autres systÚmes de caches. Exemple:: |
|---|
| 650 |
|
|---|
| 651 |
from django.views.decorators.cache import never_cache |
|---|
| 652 |
@never_cache |
|---|
| 653 |
def ma_vue(request): |
|---|
| 654 |
... |
|---|
| 655 |
|
|---|
| 656 |
.. _`spécification de Cache-Control`: http://www.w3.org/Protocols/rfc2616/rfc2616-sec14.html#sec14.9 |
|---|
| 657 |
|
|---|
| 658 |
Autres optimisations |
|---|
| 659 |
==================== |
|---|
| 660 |
|
|---|
| 661 |
Django est fourni avec plusieurs autres middlewares qui peuvent vous aider |
|---|
| 662 |
à optimiser les performances de vos applications: |
|---|
| 663 |
|
|---|
| 664 |
* ``django.middleware.http.ConditionalGetMiddleware`` ajoute le support |
|---|
| 665 |
des GET conditionnels. Ceci utilise les entêtes ``ETag`` et ``Last-Modified``. |
|---|
| 666 |
|
|---|
| 667 |
* ``django.middleware.gzip.GZipMiddleware`` compresse le contenu pour |
|---|
| 668 |
les navigateurs acceptant la compression gzip (tous les navigateurs |
|---|
| 669 |
modernes). |
|---|
| 670 |
|
|---|
| 671 |
Ordre des MIDDLEWARE_CLASSES |
|---|
| 672 |
============================ |
|---|
| 673 |
|
|---|
| 674 |
Si vous utilisez ``CacheMiddleware``, il est important de le disposer à |
|---|
| 675 |
la bonne place dans le paramÚtre de configuration ``MIDDLEWARE_CLASSES``, |
|---|
| 676 |
du fait que le middleware de cache à besoin de connaître sur quels entêtes |
|---|
| 677 |
le contenu du cache varie. Le middleware ajoute systématiquement quelque |
|---|
| 678 |
chose à l'entête ``Vary`` de la réponse dÚs qu'il le peut. |
|---|
| 679 |
|
|---|
| 680 |
Ajoutez le ``CacheMiddleware`` *avant* tout middleware pouvant ajouter |
|---|
| 681 |
quelque chose à l'entête ``Vary`` (les middlewares réponse sont appliqués en |
|---|
| 682 |
sens inverse). Les middlewares suivants sont concernés: |
|---|
| 683 |
|
|---|
| 684 |
* ``SessionMiddleware`` ajoute ``Cookie`` |
|---|
| 685 |
* ``GZipMiddleware`` ajoute ``Accept-Encoding`` |
|---|
| 686 |
* ``LocaleMiddleware`` ajoute ``Accept-Language`` |
|---|
| 687 |
|
|---|