<< précédentsuivant >>

Chaptire 13: Mise en cache

Les sites web statiques, dans lesquels de simples fichiers sont directement servis sur le web, sont incroyablement adaptables. Mais le fondement des sites web dynamiques est, et bien, qu’ils sont dynamiques. Chaque fois qu’un utilisateur demande une page, le serveur web fait toute sorte de calcul - depuis les requêtes en base en données, le rendu de gabarit, jusqu’à la logique métier - pour créer la page que verra le visiteur de votre site. Du point de vue de la surcharge de traitement, ceci est assez couteux.

Pour la majorité des applications web, cette surcharge n’est pas un vrai problème. La plupart des applications ne sont pas washingtonpost.com ou Slashdot; ce sont simplement des sites de petites à moyenne taille avec un traffic supportable. Mais pour les sites allant du traffic moyen jusqu’à conséquent, il est essentiel de minimiser la charge le plus possible. C’est ici qu’intervient la mise en chache.

Cacher quelque chose signifie sauvegarder le résultat d’un traitement couteux de façon à ce que vous n’ayez pas à effectuer ce calcul la prochaine fois. Voici un peu de pseudo-code expliquant le fonctionnement d’une page web générée dynamiquement:

pour un URL donné, essayer de trouver cette page dans le cache
si la page est dans le cache:
    renvoie la page cachée
sinon:
    génère la page
    enregistre la page générée dans le cache (pour la prochaine fois)
    renvoie la page générée

Django est fourni avec un système de cache robuste qui vous permet d’enregistrer les pages dynamiques afin qu’elles ne doivent pas calculée à chaque requête. Par commodité, Django offre différents niveaux de granularité pour le cache. Vous pouvez cacher la réponse d’une vue spécifique, vous pouvez cacher uniquement les parties qui sont difficiles à produire, ou vous pouvez cacher le site entier.

Django fonctionne aussi très bien avec les caches «en amont», tel que Squid (http://www.squid-cache.org/) et les caches basé sur les navigateurs. Ce sont des types de cache que vous ne contrôlez pas directement mais pour lesquels vous pouvez fournir des indications (via les en-têtes HTTP) au sujet des parties de votre site qui doivent être mises en cache, et sur la manière de le faire.

Lisez la suite pour découvrir comment utiliser le système de cache sous Django. Lorsque votre site sera «Slashdotté» vous serez heureux de comprendre de quoi il retourne.

Paramétrer le cache

Le système de cache nécessite un peu de paramètrage. À savoir, vous devez indiquer où doivent se trouver les données mises en cache, s’il s’agit d’une base de données, du système de fichier, ou directement de la mémoire. C’est une décision importante qui affecte les performance de votre cache (oui, certains types de cache sont plus rapides que d’autres). La mise en cache en mémoire sera généralement beaucoup plus rapide que celle en système de fichier ou en base de données, parce qu’elle évite la surcharge lié au système de fichier ou de la base de données.

Vos préférence de cache se trouve dans le paramètre CACHE_BACKEND de votre fichier de paramètres. Si vous utilisez le cache sans précisé CACHE_BACKEND, Django utilisera simple:/// par défaut. La section suivante explique toutes les valeurs disponibles pour CACHE_BACKEND.

Memcached

De loin le plus rapide et le type le plus efficace de cache disponible sous Django, Memcached est un framework de cache entièrement basé sur la mémoire et développé à l’origine pour gérer les fortes charges de LiveJournal (http://www.livejournal.com/) et ensuite rendu open-source par Danga Interactive (http://danga.com/). Il est utilisé par des sites tel que Slashdot et Wikipédia pour réduire les accès en base de données et dramatiquement augmenter les performances du site.

Memcached est disponible librement à l’adresse http://danga.com/memcached/. Il tourne en tant que démon et une quantité précise de RAM lui est alloué. Sa première caractéristique est de fournir une interface - une interface rapide commme la lumière - pour ajouter, récupérer et supprimer des données arbitraires du cache. Toutes les données sont stockées directement en mémoire, il n’y a donc pas de surcharge liée à la base de données ou au système de fichier.

Après avoir installé Memcached, vous devrez installer la liaison Python Memcached, qui n’est pas directement livrée avec Django. Ces liaisons se trouvent dans un unique module Python, memcache.py, disponible à l’adresse http://www.tummy.com/Community/software/python-memcached/.

Pour utiliser Memcached avec Django, placez CACHE_BACKEND à memcached://ip:port/, où ip est l’adresse IP du démon Memcached et port est le port sur lequel tourne Memcached.

Dans cet exemple, Memcached tourne sur le port 11211 de localhost (127.0.0.1):

CACHE_BACKEND = 'memcached://127.0.0.1:11211/'

Une fonctionnalité excellente de Memcached est sa capacité à partager le cache sur de multiples serveurs. Cela signifi que vouspouvez lancer les démons Memcached sur de multiples machines, et que le programme traitera le groupe de machines comme un cache unique, sans avoir besoin de dupliquer les valeurs de cache sur chacune des machines. Pour tirer partie de cette fonctionnalité sous Django, incluez toutes les adresses de serveur dans CACHE_BACKEND, séparées par des point virgules.

Dans cet exemple, la cache est partagé sur les instances memcached tournant sur les adresses IP 172.19.26.240 et 172.19.26.242, toutes deux sur le port 11211:

CACHE_BACKEND = 'memcached://172.19.26.240:11211;172.19.26.242:11211/'

Dans l’exemple suivant, le cache est réparti sur les instances Memcached tournant sur les adresses IP 172.19.26.240 (port 11211), 172.19.26.242 (port 11212), et 172.19.26.244 (port 11213):

CACHE_BACKEND = 'memcached://172.19.26.240:11211;172.19.26.242:11212;172.19.26.244:11213/'

Une dernière chose au sujet de Memcached est que le cache basé sur la mémoire présente un désavantage important. Puisque les données mises en cache sont stockées uniquement en mémoire, les données seront perdues si votre serveur plante. Clairement, la mémoire n’est pas destinée à un stockage permanent des données, aussi ne comptez pas sur le cache en mémoire comme unique source de stockage des données. Sans aucun doute, aucun des moteurs de cache Django ne doit être utilisé pour le stockage permanent - ils sont tous destinés à la mise en cache, et non pas au stockage - mais nous éclairons ce point parce que la mise en cache en mémoire est particulièrement temporaire.

Cache en base de données

Pour utiliser une table de la base de données en guise de cache, créez une table cache dans votre base et pointez le système de cache Django sur celle-ci:

Tout d’abord, créez une table cache grâce à cette commande:

python manage.py createcachetable [cache_table_name]

[cache_table_name] est le nom de la table de la base de données à créer. Ce nom peut être celui que vous désirez, du moment qu’il sagisse d’un nom de table valide qui ne soit pas déjà utilisé dans votre base de données. Cette commande crée un table unique dans la base, au format propre au système de cache que Django attends.

Une fois que vous avez créez la table, fixez votre paramètre CACHE_BACKEND à "db://tablename", où tablename est le nom de table de votre base de données. Dans cet exemple, le nom de la table est my_cache_table:

CACHE_BACKEND = 'db://my_cache_table'

L’arrière-plan de la mise en cache en base de données utilise la même base que celle précisée dans votre fichier de paramètres. Vous ne pouvez pas utiliser un arrière-plan de base de données différent pour votre table de cache.

Cache sur le système de fichier

Pour stocker les éléments cachés sur un système de fichier, utilisez le type de cache "file://" en guise de CACHE_BACKEND, en précisant le répertoire de votre système de fichier qui doit stocker les données mises en cache.

Par exemple, pour stocker les données cachées dans /var/tmp/django_cache, utilisez ce paramètre:

CACHE_BACKEND = 'file:///var/tmp/django_cache'

Notez les trois barres obliques au début de l’exemple précédent. Les deux premiers correspondent à file://, et le troisième correspond au premier caractère du chemin vers le répertoire, /var/tmp/django_cache. Si vous êtes sous Windows, placez la lettre correspondant au lecteur après file://, comme ceci: file://c:/foo/bar.

Le chemin du répertoire doit être absolut - c’est à dire qu’il doit commencer à la racine de votre système de fichier. Que vous placiez une barre oblique à la fin du paramètre importe peu.

Assurez vous que le répertoire sur lequel pointe ce paramètre existe bine et qu’il soit accessible en lecture et en écriture par l’utilisateur système sous lequel tourne votre serveur web. En continuant avec l’exemple précédent, si votre serveur tourne sous l’utilisateur apache, assurez vous que le répertoire /var/tmp/django_cache existe et est accessible à l’utilisateur apache en écriture et en lecture.

Chaque valeur de cache sera stocké dans un fichier séparé dont le contenu sont les données du cache sérialisées («picklées»), grâce au module Python pickle. Chaque nom de fichier est la clef du cache, échappé pour être utilisé sans soucis par le système de fichier.

Cache en mémoire locale

Si vous voulez les avantages de la vitesse du cache en mémoire sans avoir la possibilité d’utiliser Memcached, pensez au cache mémoire local. Ce cache est multi-processus et supporte les thread, mais n’est pas aussi efficace que Memcached du fait de son système de verrous simpliste et de sa stratégie d’allocation de la mémoire.

Pour l’utiliser, fixez CACHE_BACKEND tà``’locmem:///’``, par exemple:

System Message: WARNING/2 (<string>, line 216); backlink

Inline literal start-string without end-string.
CACHE_BACKEND = 'locmem:///'

Cache simple (pour le développement)

Un cache mémoire simple, mono processus, est diponible grâce à 'simple:///', par exemple:

CACHE_BACKEND = 'simple:///'

Ce cache sauve uniquement les données cachées dans un processus, ce qui signifie qu’il doit être utilisé uniquement lors du développement ou dasn des environnements de tests.

Cache factice (pour le développement)

Finalement, Django est fourni avec un cache «factice» qui en fait ne cache rien; il implémente simplement l’interface de cache sans rien faire d’autre.

Ceci est pratique si vous avez un site en production qui utilise massivement le cache en divers endroits et un environnement de développement/test pour lequel vous ne voulez pas utiliser le cache. Dans ce cas, fixez CACHE_BACKEND à 'dummy:///' dans le fichier de paramètres de votre environnement de développement, par exemple:

CACHE_BACKEND = 'dummy:///'

En conséquence, votre environnement de développement n’utilisera pas le cache, alors que votre environnement de production continuera à le faire.

Arguments de CACHE_BACKEND

Chaque arrière-plan de mise en cache peut prendre des arguments. Ils sont précisés dans le paramètre CACHE_BACKEND sous forme de chaîne de requête. Les arguments possibles sont les suivants:

  • timeout: le délai d’expiration par défaut, en seconde, à utiliser pour le cache. La valeur par défaut de cet argument est de 300 seconds (5 minutes).

  • max_entries: pour la mémoire locale, simple, et pour la base de données, le nombre maximal d’entrées autorisées dans le cache avant que les anciennes valeurs soient supprimé. La valeur par defaut de cet argument est 300.

  • cull_frequency: le ratio entre les entrées qui sont éliminées lorsque max_entries est atteinte. Le ration actuel est 1/cull_frequency, donc fixez cull_frequency=2 pour éliminier la moitié des entrées lorsque max_entries est atteinte.

    Une valeur à 0 pour cull_frequency signifie que l’intégralité du cache sera supprimé lorsque max_entries est atteint. Cela rends la suppression beaucoup plus rapide au détriment d’un plus grand manque de cache. La valeur de cet argement est 3 par défaut.

Dans cet exemple, timeout vaut 60:

CACHE_BACKEND = "locmem:///?timeout=60"

Dans cet exemple, timeout vaut 30 et max_entries vaut 400:

CACHE_BACKEND = "locmem:///?timeout=30&max_entries=400"

Les arguments invalides sont silencieusement ignorés, tout comme les valeurs invalides des arguments connus.

Cache par site

Une fois que vous avez spécifié CACHE_BACKEND, la façon la plus simple d’utiliser le cache est de mettre en cache l’intégralité de votre site. Cela signifie que chaque page qui n’a pas de paramètre GET ou POST sera caché pendant un certain temps la première fois qu’elle est demandé.

Pour activer le cache par site, ajoutez simplement 'django.middleware.cache.CacheMiddleware' à votre paramètre MIDDLEWARE_CLASSES, comme dans cet exemple:

 MIDDLEWARE_CLASSES = (
    'django.middleware.cache.CacheMiddleware',
    'django.middleware.common.CommonMiddleware',
)

System Message: ERROR/3 (<string>, line 324)

The “admonition” admonition is empty; content required.

.. admonition:: Note

La position de MIDDLEWARE_CLASSES compte. Lisez la section «ordre de MIDDLEWARE_CLASSES» plus loin dans ce chapitre.

Ensuite, ajoutez les paramètres requis suivants à votre fichier de paramètres Django:

  • CACHE_MIDDLEWARE_SECONDS: le nombre de secondes pendant lequel chaque page doit être cachée.
  • CACHE_MIDDLEWARE_KEY_PREFIX: si le cache est partagé sur de multiples sites utilisant la même installation Django, fixer ce paramètre selon le nom du site, ou selon n’importe quelle autre chaîne qui soit unique pour cette instance de Django, pour prévenir les collisions de clefs. Utilisez une chaîne vide si cela ne vous concerne pas.

Le middleware de cache met en cache chaque page qui ne possède pas de paramètres GET ou POST. Ainsi, si un utilisateur demande une page et transmet des paramètres dans une chaîne de requête, ou transmet des paramètres POST, le middleware n’essaiera pas de récupérer une version cachée de la page. Si vous cherchez à utiliser le cache par site, gardez ceci à l’esprit lorsque vous concevez votre application; n’utilisez par d’URLs avec une chaîne de requête, par exemple, à moins qu’il soit acceptable pour votre application de ne pas mettre en cache ces pages.

Le middleware de cache accepte un autre paramètre, CACHE_MIDDLEWARE_ANONYMOUS_ONLY. Si vous avez défini ce paramètre, et qu’il est fixé à True, alors le middleware de cache cachera uniquement les requêtes anonymes (c’est à dire que les requêtes faites par un utilisateur non connecté). C’est une façon simple et efficace de désactiver le cache pour toutes pages spécifiques à l’utilisateur, telle que l’interface d’administration de Django. Notez que si vous utilisez CACHE_MIDDLEWARE_ANONYMOUS_ONLY, vous devez vous assurer que AuthenticationMiddleware soit activé et que AuthenticationMiddleware apparaisse avant CacheMiddleware dans votre MIDDLEWARE_CLASSES.

Finalement, notez que CacheMiddleware définit automatiquement quelques en-têtes dans chaque HttpResponse:

  • il définit l’en-tête Last-Modified selon la date/heure courante lorsqu’une version fraîche (inexistante en cache) est demandé.
  • il définit l’en-tête Expires selon la date/heure courante et selon CACHE_MIDDLEWARE_SECONDS.
  • il définit l’en-tête Cache-Control pour donner un age maximal à la page, toujours selon le paramètre CACHE_MIDDLEWARE_SECONDS.

Cache par vue

Une manière plus granulaire d’utiliser le framework de consiste à cacher la sortie des vues individuelles. Ceci à le même effet que le cache par site, (y compris l’ommission du cache sur les requêtes présentant des paramètres GET et POST). Elle s’applique à toutes vues que vous préciserez, plutôt qu’à l’intégralité du site.

Vous ferez ceci en utilisant un décorateur, qui est un emballage autour de la vue et qui altère la façon dont elle utilise le cache. Le décorateur de cache par vue est appelé cache_page et se trouve dans le module django.views.decorators.cache, par exemple:

from django.views.decorators.cache import cache_page

def my_view(request, param):
    # ...
my_view = cache_page(my_view, 60 * 15)

Alternativement, si vous utilisez Python 2.4 et suivant, vous pouvez utilisez la syntaxe de décorateur. Cet exemple est équivalent au précédent:

from django.views.decorators.cache import cache_page

@cache_page(60 * 15)
def my_view(request, param):
    # ...

cache_page prends un unique argument: l’expiration du cache, en secondes. Dans l’exemple précédent, le résultat de la vue my_view() sera mis en cache pour 15 minutes. (Notez que nous l’avons écrit 60 * 15 pour des raisons de lisibilité. 60 * 15 sera évalué à 900 - c’est à dire 15 minutes multiplié parr 60 secondes par minute).

Le cache par vue, tout comme le cache par site, est lié à l’URL. Si de multiples URLs pointent sur la même vue, chaque URL sera caché séparément. En continuant avec l’exemple my_view, si votre URLconf ressemble à ceci:

urlpatterns = ('',
    (r'^foo/(\d{1,2})/$', my_view),
)

alors les requêtes vers /foo/1/ et /foo/23/ seront mises en cache séparement, comme vous pouvez vous y attendre. Mais lorsqu’un URL particulier (par exemple, /foo/23/) à été requis, les requêtes suivantes vers cet URL utiliserons le cache.

Préciser un cache par vue dans l’URLconf

Les exemples de la section précédente ont codé en dur le fait que la vue soit mise en cache, puisque cache_page altère la fonction my_view en place. Cette approche couple votre vue au système de cache, ce qui n’est pas idéal pour plusieurs raisons. Par exemple, vous pourriez vouloir réutiliser la vue dans un autre site, sans cache, ou vous pourriez vouloir distribuer les vues à des gens qui voudrons les utiliser sans être mises en cache. La solutions à ces problèmes est de spécifier le cache par vue dans l’URLconf plutôt qu’à côté des fonctions vue elles-mêmes.

Réaliser ceci est très facile: emballez simplement la fonction vue avec un cache_page lorsque vous y faite référence dans l’URLconf. Voici l’ancien URLconf utilisé plus tôt:

urlpatterns = ('',
    (r'^foo/(\d{1,2})/$', my_view),
)

Voici la même chose, avec my_view emballé dans cache_page:

from django.views.decorators.cache import cache_page

urlpatterns = ('',
    (r'^foo/(\d{1,2})/$', cache_page(my_view, 60 * 15)),
)

Si vous prenez cette approche, n’oubliez pas d’importer cache_page à l’intérieur de votre URLconf.

L’API de cache bas-niveau

Parfois, mettre en cache une page entièrement générée ne vous fait gagner grand chose et, en fait, peut être même génant.

Peut être que, par exemple, votre site propose une vue dont le résultat dépend de plusieurs requêtes expensives, et dont les résultats changent selon l’interval. Dans ce cas, il ne serait pas idéal d’utiliser la mise en cache de la page entière qu’offrent les stratégies de cache par site ou par vue parce que vous ne voudriez pas cacher le résultat entier (car quelques données changent souvent), mais vous vouler tout de même mettre en cache ce qui change rarement.

Pour des cas comme ceux-ci, Django expose une API de cache simple, de bas niveau, qui se trouve dans le module django.core.cache. Vous pouvez utiliser cette API pour stocker des objets dans le cache quelque soit le niveau de granularité que vous vouliez. Vous pouvez cacher tout objet Python qui peut être conservé («picklé»): les chaînes, les dictionnaires, les listes d’objets modèle, et ainsi de suite. (La plupart des objets Python courants peuvent être conservés; référrez vous à la documentation Python pour en savoir plus au sujet du pickling).

Voici comment importer l’API:

>>> from django.core.cache import cache

l’interface de base est set(key, value, timeout_seconds) et get(key):

>>> cache.set('my_key', 'hello, world!', 30)
>>> cache.get('my_key')
'hello, world!'

L’argument timeout_seconds est optionnel et vaut par défaut l’argument timeout du paramètre CACHE_BACKEND détaillé plus avant.

Si l’objet n’existe pas dans le cache, ou si le moteur de cache est inacessible, cache.get() retourne None:

# Wait 30 seconds for 'my_key' to expire...

>>> cache.get('my_key')
None

>>> cache.get('some_unset_key')
None

Nous vous déconseillons de stocker la valeur littérale None dans le cache, parce que vous ne pourrez pas distinguer la valeur None stockée et une absence de cache indiquée par la valeur None retournée.

cache.get() accepte un argument default. Il précise quelle est la valeur à retourner si l’objet n’existe pas dans le cache:

>>> cache.get('my_key', 'has expired')
'has expired'

Pour récupérer de multiples valeurs du cache en une seule fois; utilisez cache.get_many(). Si le moteur de cache donné le permet, get_many() interrogera le cache une seule fois, contrairement à l’interrogation de la clef par cache. get_many() renvoie un dictionnaire avec toutes les clefs demandées qui existent dans le cache et qui n’ont pas expiré:

>>> cache.set('a', 1)
>>> cache.set('b', 2)
>>> cache.set('c', 3)
>>> cache.get_many(['a', 'b', 'c'])
{'a': 1, 'b': 2, 'c': 3}

Si une clef du cache n’existe pas ou a expiré, elle ne sera pas incluse dans le dictionnaire. L’extrait suivant est la suite de l’exemple:

>>> cache.get_many(['a', 'b', 'c', 'd'])
{'a': 1, 'b': 2, 'c': 3}

Enfin, vous pouvez effacer des clefs explicitement grâce à cache.delete(). C’est une manière simple de purger le cache pour un objet particulier:

>>> cache.delete('a')

cache.delete() n’a pas de valeur de retour, et fonctionne de la même façon que la valeur existe ou pas pour une clef de cache donnéee.

caches amonts

Pour l’instant, ce chapitre s’est concentré sur le cache de vos propres données. Mais un autre type de cache est aussi pertinent en matière de développement web: le cache utilisant les caches amonts. ce sont des systèmes qui cachent les pages pour les utilisateurs avant même que la requête parvienne à votre site web.

Voici quelques exemples de caches amonts:

  • Votre FAI peut mettre certaines pages en cache, si vous demandez une page sur http://example.com/, votre FAI vous enverra la page sans accéder directement à example.com. Le mainteneur d’example.com ne sait rien de cette mise en cache; le FAI est placé entre example.com et votre navigateur web, gérant tout le cache de manière transparente.
  • Votre site web sous Django peut être placé derrière un proxy cache, tel que Squid (http://www.squid-cache.org/), qui cache les pages pour améliorer les performances. Dans ce cas, chaque requête sera d’abord géré par le proxy, et ensuite transmise à votre application seulement si nécessaire.
  • Votre navigateur web cache les pages, lui aussi. Si une page web envoie présente les en-têtes appropriées, votre navigateur utilisera la copie cachée en locale pour les requêtes ultérieures sur cette page, sans même interroger la page web à nouveau pour voir si elle a changé.

La mise en cache amont est un accélérateur efficace, mais pas sans danger. Les contenus de beaucoup de pages web diffèrent selon l’identification et autres variables, aussi les systèmes de cache qui sauvegardent aveuglément les pages purement basées sur les URLs peuvent présenter des données incorrectes ou sensibles aux visiteurs suivants de ces mêmes pages.

Par exemple, admettons que vous utilisiez un service de courrier sur le web, et que le contenu de la page «inbox» dépend évidemment de l’utilisateur qui s’y connecte. Si un FAI cachait aveuglément votre site, alors le premier utilisateur connecté via ce FAI verra sa page spécifique «inbox» mise en cache pour les visiteurs suivants de ce site. Ce n’est pas sympa.

Heureusement, HTTP fourni une solution à ce problème. Un nombre d’en-têtes HTTP existe pour renseigner les caches amonts de différer leur contenu de cache selon des variables désignées, et pour dire aux mécanismes de cache de ne pas mettre en cache ces pages particulières. Nous détaillerons quelques unes de ces en-têtes dans les sections qui suivent.

Utiliser les en-têtes Vary

L’en-tête Vary défini quelles en-têtes de requête le mécanisme de cache doit prendre en compte lorsqu’il construit son cache. Par exemple, si le contenu d’une page web dépends des préférences de langues de l’utilisateur, la page est dite «vary»able selon la langue.

Par défaut, le système de cache de Django crée ses clefs de cache en utilisant le chemin demandé (par exemple, "/stories/2005/jun/23/bank_robbed/"). Cela signifie que cet URL utilisera la même version cachée, selon les différences avec l’agent utilisateur tels que les cookies ou les préférences de langue. Cependant, si cette page produit des contenus différents basés sur des différences dans les en-tête de la requête - comme les cookies, ou une langue, ou un agent utilisateur - vous devrez utiliser l’en-tête Vary pou indiquer aux mécanismes de cache que la page rendue dépends de cela.

Pour faire cela sous Django, utilisez le très pratique décorateur vary_on_headers dans la vue, comme ceci:

from django.views.decorators.vary import vary_on_headers

# Python 2.3 syntax.
def my_view(request):
    # ...
my_view = vary_on_headers(my_view, 'User-Agent')

# Python 2.4+ decorator syntax.
@vary_on_headers('User-Agent')
def my_view(request):
    # ...

Dans ce cas, un mécanisme de cache (tel que le propre middleware de cache de Django) placera en cache une version séparée de la page pour cache agent utilisateur unique.

L’avantage d’utiliser le décorateur vary_on_headers au lieu de paramétrer manuellement l’en-tête Vary (en utilisant quelque chose du genre response['Vary'] = 'user- agent') est que le décorateur ajoute à l’en-tête Vary (qui peut déjà exister), plutôt que le créer à partir de rien et potentiellement écraser tout ce qui pouvait être déjà dedans.

Vous pouvez passer de multiples en-têtes à vary_on_headers():

@vary_on_headers('User-Agent', 'Cookie')
def my_view(request):
    # ...

Ceci précise aux caches amonts de «vary»er pour les deux, ce qui signifie que chaque combinaison d’agent utilisateur et de cookie obtiendra sa propre valeur de cache. Par exemple, une requête avec l’agent utilisateur Mozilla avec la valeur de cookie foo=bar sera considérée comme différente d’une requête avec l’agent Mozilla et la valeur de cookie foo=ham.

Puisque «vary»er sur les cookies est chose courante, il existe un décorateur vary_on_cookie. Ces deux vues sont équivalentes:

@vary_on_cookie
def my_view(request):
    # ...

@vary_on_headers('Cookie')
def my_view(request):
    # ...

Les en-têtes que vous passez à vary_on_headers ne sont pas sensibles à la casse; "User-Agent" est identique à "user-agent".

Vous pouvez aussi utiliser une fonction d’assistance, django.utils.cache.patch_vary_headers, directement. Cette fonction fixe, ou ajoute à, Vary header, par exemple:

from django.utils.cache import patch_vary_headers

def my_view(request):
    # ...
    response = render_to_response('template_name', context)
    patch_vary_headers(response, ['Cookie'])
    return response

patch_vary_headers accepte une instance de HttpResponse pour premier argument et une liste/tuple de nom d’en-tête insensible à la casse pour second argument.

Autres en-têtes de cache

Les autres problèmes avec la mise en cache sont la confidentialité des données et la question de l’emplacement de stockage dans une cascace de caches.

Un utilisateur fait habituellement face à ces deux types de caches: celui de son navigateur (le cache privé) et celui de son fournisseur d’accès (le cache public). Un cache public est utilisé par de multiples utilisateurs et contrôlé par un tiers. Ceci pose problème lorsqu’il s’agit de données sensibles que vous ne voulez pas, comme par exemple votre numéro de compte banquaire, stocker dans un cache publique. Ainsi les applications web ont besoin de connaitre un moyen de dire aux caches quelles données sont privées et quelles données sont publique.

La solution consiste à préciser que le cache de la page doit être «privé». Pour ce faire avec Django, utilsez le décorateur de vue cache_control:

from django.views.decorators.cache import cache_control

@cache_control(private=True)
def my_view(request):
    # ...

Ce décorateur prends soins d’envoyer l’en-tête HTTP approprié en coulisses.

Il y à d’autres façons de contrôler les paramètres du cache. Par exemple, HTTP autorise les applications à faire les choses suivantes:

  • définir la durée de vie maximum de la page cachée.
  • préciser si un cache doit toujours vérifier s’il éxiste de nouvelles versions, ne délivrant le contenu caché que lorsqu’il n’y a pas de changements (certains caches peuvent délivrer du contenu caché même si la page sur le serveur a changé, simplement parce que le délai de la copie cachée n’a pas encore expiré).

Sous Django, utilisez le décorateur de vue cache_control pour préciser ces paramètres de cache. Dans cet exemple, cache_control indique aux caches qu’il faut revalider le cache lors de chaque accès et qu’il faut stocker les versions cachées durant, au moins, 3 600 secondes:

from django.views.decorators.cache import cache_control
@cache_control(must_revalidate=True, max_age=3600)
def my_view(request):
    ...

Toute directive HTTP Cache-Control valide est permise dans cache_control(). Voici une liste complète:

  • public=True
  • private=True
  • no_cache=True
  • no_transform=True
  • must_revalidate=True
  • proxy_revalidate=True
  • max_age=num_seconds
  • s_maxage=num_seconds

Astuce

Pour des explications au sujet des directives HTTP Cache-Control, lisez la spécification à l’adresse http://www.w3.org/Protocols/rfc2616/rfc2616-sec14.html#sec14.9.

System Message: ERROR/3 (<string>, line 780)

The “admonition” admonition is empty; content required.

.. admonition:: Note

Le middleware de cache fixe déjà les en-têtes de cache max-age` avec la valeur du paramètre CACHE_MIDDLEWARE_SETTINGS. Si vous utilisez un max_age personnalisé dans un décorateur cache_control, le décorateur prendra précédence, et les valeurs de l’en-tête seront correctement fusionnées).

Autres optimisations

Django est fourni avec quelques autres composants de middleware qui peuvent aider à optimiser les performances de vos applications:

  • django.middleware.http.ConditionalGetMiddleware ajoute le support des navigateurs modernes pour obtenir conditionnellement les réponses GET basées sur les en-têtes ETag et Last-Modified.
  • django.middleware.gzip.GZipMiddleware compresse les réponses pour tous les navigateurs modernes, épargant ainsi de la bande passante et du temps de transfert.

Ordre des MIDDLEWARE_CLASSES

Si vous utilisez CacheMiddleware, il est important de placer celui-ci au bon endroit au sein du paramètre MIDDLEWARE_CLASSES, parce que le middleware de cache doit connaître les en-têtes qui doivent faire varier le stockage en cache.

Placez le CacheMiddleware après tout middleware qui peut ajouter quelque chose à l’en-tête Vary, parmi lesquels on trouve:

  • SessionMiddleware,qui ajoute Cookie
  • GZipMiddleware, qui ajoute Accept-Encoding

Et ensuite ?

Django est livré avec tout un tas de sympathiques «paquets de contributions», aux fonctionnalités optionnelles. Nous en avons déjà abordé une partie: du système d’administration (au chapitre 6) et du framework session/utilisateur (au cahpitre 11).

Le prochain chapitre couvre le reste des sous frameworks. De nombreux outils super sympas sont disponibles; vous n’en manquerez pas un.

<< précédentsuivant >>

Dernière modification: 2008-08-05 15:08:25.586310