Redis
 sql >> Base de données >  >> NoSQL >> Redis

Mise en cache dans Django avec Redis

Les performances des applications sont essentielles au succès de votre produit. Dans un environnement où les utilisateurs s'attendent à des temps de réponse du site Web inférieurs à une seconde, les conséquences d'une application lente peuvent être mesurées en dollars et en cents. Même si vous ne vendez rien, le chargement rapide des pages améliore l'expérience de visite de votre site.

Tout ce qui se passe sur le serveur entre le moment où il reçoit une requête et le moment où il renvoie une réponse augmente le temps de chargement d'une page. En règle générale, plus vous pouvez éliminer de traitement sur le serveur, plus votre application fonctionnera rapidement. La mise en cache des données après leur traitement, puis leur diffusion à partir du cache la prochaine fois qu'elles sont demandées, est un moyen de soulager le stress sur le serveur. Dans ce didacticiel, nous explorerons certains des facteurs qui ralentissent votre application et nous montrerons comment mettre en œuvre la mise en cache avec Redis pour contrer leurs effets.

Bonus gratuit : Cliquez ici pour accéder à un guide gratuit des ressources d'apprentissage Django (PDF) qui vous montre des trucs et astuces ainsi que les pièges courants à éviter lors de la création d'applications Web Python + Django.


Qu'est-ce que Redis ?

Redis est un magasin de structure de données en mémoire qui peut être utilisé comme moteur de mise en cache. Puisqu'il conserve les données dans la RAM, Redis peut les fournir très rapidement. Redis n'est pas le seul produit que nous pouvons utiliser pour la mise en cache. Memcached est un autre système de mise en cache en mémoire populaire, mais de nombreuses personnes conviennent que Redis est supérieur à Memcached dans la plupart des cas. Personnellement, nous apprécions la facilité de configuration et d'utilisation de Redis à d'autres fins telles que la file d'attente Redis.



Mise en route

Nous avons créé un exemple d'application pour vous présenter le concept de mise en cache. Notre application utilise :

  • Django (v1.9.8)
  • Barre d'outils de débogage Django (v1.4)
  • django-redis (v4.4.3)
  • Redis (v3.2.0)

Installer l'application

Avant de cloner le référentiel, installez virtualenvwrapper, si vous ne l'avez pas déjà. Il s'agit d'un outil qui vous permet d'installer les dépendances Python spécifiques dont votre projet a besoin, vous permettant de cibler les versions et les bibliothèques requises par votre application de manière isolée.

Ensuite, modifiez les répertoires dans lesquels vous conservez les projets et clonez l'exemple de référentiel d'applications. Une fois cela fait, modifiez les répertoires vers le référentiel cloné, puis créez un nouvel environnement virtuel pour l'exemple d'application à l'aide de mkvirtualenv commande :

$ mkvirtualenv django-redis
(django-redis)$

REMARQUE : Créer un environnement virtuel avec mkvirtualenv l'active également.

Installez toutes les dépendances Python requises avec pip , puis consultez la balise suivante :

(django-redis)$ git checkout tags/1

Terminez la configuration de l'exemple d'application en créant la base de données et en la remplissant avec des exemples de données. Assurez-vous également de créer un superutilisateur afin de pouvoir vous connecter au site d'administration. Suivez les exemples de code ci-dessous, puis essayez d'exécuter l'application pour vous assurer qu'elle fonctionne correctement. Visitez la page d'administration du navigateur pour confirmer que les données ont été correctement chargées.

(django-redis)$ python manage.py makemigrations cookbook
(django-redis)$ python manage.py migrate
(django-redis)$ python manage.py createsuperuser
(django-redis)$ python manage.py loaddata cookbook/fixtures/cookbook.json
(django-redis)$ python manage.py runserver

Une fois l'application Django en cours d'exécution, passez à l'installation de Redis.



Installer Redis

Téléchargez et installez Redis en suivant les instructions fournies dans la documentation. Vous pouvez également installer Redis à l'aide d'un gestionnaire de packages tel que apt-get ou homebrew selon votre système d'exploitation.

Exécutez le serveur Redis à partir d'une nouvelle fenêtre de terminal.

$ redis-server

Ensuite, démarrez l'interface de ligne de commande (CLI) Redis dans une autre fenêtre de terminal et testez qu'elle se connecte au serveur Redis. Nous utiliserons la CLI Redis pour inspecter les clés que nous ajoutons au cache.

$ redis-cli ping
PONG

Redis fournit une API avec diverses commandes qu'un développeur peut utiliser pour agir sur le magasin de données. Django utilise django-redis pour exécuter des commandes dans Redis.

En regardant notre exemple d'application dans un éditeur de texte, nous pouvons voir la configuration Redis dans settings.py dossier. On définit un cache par défaut avec le CACHES paramètre, en utilisant un django-redis intégré cache comme notre backend. Redis s'exécute sur le port 6379 par défaut, et nous pointons vers cet emplacement dans notre configuration. Une dernière chose à mentionner est que django-redis ajoute aux noms de clé un préfixe et une version pour aider à distinguer les clés similaires. Dans ce cas, nous avons défini le préfixe comme "exemple".

CACHES = {
    "default": {
        "BACKEND": "django_redis.cache.RedisCache",
        "LOCATION": "redis://127.0.0.1:6379/1",
        "OPTIONS": {
            "CLIENT_CLASS": "django_redis.client.DefaultClient"
        },
        "KEY_PREFIX": "example"
    }
}

REMARQUE  :Bien que nous ayons configuré le backend du cache, aucune des fonctions d'affichage n'a implémenté la mise en cache.




Performances de l'application

Comme nous l'avons mentionné au début de ce tutoriel, tout ce que fait le serveur pour traiter une requête ralentit le temps de chargement de l'application. La surcharge de traitement liée à l'exécution de la logique métier et des modèles de rendu peut être importante. La latence du réseau affecte le temps nécessaire pour interroger une base de données. Ces facteurs entrent en jeu chaque fois qu'un client envoie une requête HTTP au serveur. Lorsque les utilisateurs lancent de nombreuses requêtes par seconde, les effets sur les performances deviennent perceptibles car le serveur s'efforce de les traiter toutes.

Lorsque nous implémentons la mise en cache, nous laissons le serveur traiter une requête une fois, puis nous la stockons dans notre cache. Comme les requêtes pour la même URL sont reçues par notre application, le serveur extrait les résultats du cache au lieu de les traiter à nouveau à chaque fois. En règle générale, nous définissons une durée de vie sur les résultats mis en cache, afin que les données puissent être actualisées périodiquement, ce qui est une étape importante à mettre en œuvre afin d'éviter de diffuser des données obsolètes.

Vous devriez envisager de mettre en cache le résultat d'une requête lorsque les cas suivants sont vrais :

  • l'affichage de la page implique de nombreuses requêtes de base de données et/ou une logique métier,
  • la page est fréquemment visitée par vos utilisateurs,
  • les données sont les mêmes pour chaque utilisateur,
  • et les données ne changent pas souvent.

Commencez par mesurer les performances

Commencez par tester la vitesse de chaque page de votre application en comparant la rapidité avec laquelle votre application renvoie une réponse après avoir reçu une requête.

Pour y parvenir, nous allons envoyer à chaque page une rafale de requêtes à l'aide de loadtest, un générateur de charge HTTP, puis porter une attention particulière au taux de requêtes. Visitez le lien ci-dessus pour installer. Une fois installé, testez les résultats par rapport au /cookbook/ Chemin URL :

$ loadtest -n 100 -k  http://localhost:8000/cookbook/

Notez que nous traitons environ 16 requêtes par seconde :

Requests per second: 16

Lorsque nous examinons ce que fait le code, nous pouvons prendre des décisions sur la manière d'apporter des modifications pour améliorer les performances. L'application effectue 3 appels réseau vers une base de données avec chaque demande à /cookbook/ , et il faut du temps à chaque appel pour ouvrir une connexion et exécuter une requête. Visitez le /cookbook/ URL dans votre navigateur et développez l'onglet Django Debug Toolbar pour confirmer ce comportement. Trouvez le menu intitulé "SQL" et lisez le nombre de requêtes :

livre de recettes/services.py

from cookbook.models import Recipe


def get_recipes():
    # Queries 3 tables: cookbook_recipe, cookbook_ingredient,
    # and cookbook_food.
    return list(Recipe.objects.prefetch_related('ingredient_set__food'))

livre de recettes/views.py

from django.shortcuts import render
from cookbook.services import get_recipes


def recipes_view(request):
    return render(request, 'cookbook/recipes.html', {
        'recipes': get_recipes()
    })

L'application affiche également un modèle avec une logique potentiellement coûteuse.

<html>
<head>
  <title>Recipes</title>
</head>
<body>
{% for recipe in recipes %}
  <h1>{{ recipe.name }}</h1>
    <p>{{ recipe.desc }}</p>
  <h2>Ingredients</h2>
  <ul>
    {% for ingredient in recipe.ingredient_set.all %}
    <li>{{ ingredient.desc }}</li>
    {% endfor %}
  </ul>
  <h2>Instructions</h2>
    <p>{{ recipe.instructions }}</p>
{% endfor %}
</body>
</html>


Mettre en œuvre la mise en cache

Imaginez le nombre total d'appels réseau que notre application effectuera lorsque les utilisateurs commenceront à visiter notre site. Si 1 000 utilisateurs accèdent à l'API qui récupère les recettes de livres de cuisine, notre application interrogera la base de données 3 000 fois et un nouveau modèle sera rendu à chaque requête. Ce nombre ne fait qu'augmenter à mesure que notre application évolue. Heureusement, cette vue est un excellent candidat pour la mise en cache. Les recettes d'un livre de cuisine changent rarement, voire jamais. De plus, comme l'affichage des livres de cuisine est le thème central de l'application, l'API récupérant les recettes est garantie d'être appelée fréquemment.

Dans l'exemple ci-dessous, nous modifions la fonction d'affichage pour utiliser la mise en cache. Lorsque la fonction s'exécute, elle vérifie si la clé de vue est dans le cache. Si la clé existe, l'application récupère les données du cache et les renvoie. Si ce n'est pas le cas, Django interroge la base de données puis stocke le résultat dans le cache avec la clé de vue. La première fois que cette fonction est exécutée, Django interrogera la base de données et restituera le modèle, puis effectuera également un appel réseau à Redis pour stocker les données dans le cache. Chaque appel ultérieur à la fonction contournera complètement la base de données et la logique métier et interrogera le cache Redis.

exemple/settings.py

# Cache time to live is 15 minutes.
CACHE_TTL = 60 * 15

livre de recettes/views.py

from django.conf import settings
from django.core.cache.backends.base import DEFAULT_TIMEOUT
from django.shortcuts import render
from django.views.decorators.cache import cache_page
from cookbook.services import get_recipes

CACHE_TTL = getattr(settings, 'CACHE_TTL', DEFAULT_TIMEOUT)


@cache_page(CACHE_TTL)
def recipes_view(request):
    return render(request, 'cookbook/recipes.html', {
        'recipes': get_recipes()
    })

Notez que nous avons ajouté le @cache_page() décorateur à la fonction de vue, ainsi qu'un temps de vie. Visitez le /cookbook/ URL à nouveau et examinez la barre d'outils de débogage Django. Nous voyons que 3 requêtes de base de données sont effectuées et 3 appels sont effectués vers le cache afin de vérifier la clé puis de la sauvegarder. Django enregistre deux clés (1 clé pour l'en-tête et 1 clé pour le contenu de la page rendue). Rechargez la page et observez comment l'activité de la page change. La deuxième fois, 0 appels sont effectués vers la base de données et 2 appels sont effectués vers le cache. Notre page est maintenant servie depuis le cache !

Lorsque nous réexécutons nos tests de performances, nous constatons que notre application se charge plus rapidement.

$ loadtest -n 100 -k  http://localhost:8000/cookbook/

La mise en cache a amélioré la charge totale, et nous résolvons désormais 21 requêtes par seconde, soit 5 de plus que notre référence :

Requests per second: 21


Inspecter Redis avec la CLI

À ce stade, nous pouvons utiliser la CLI Redis pour examiner ce qui est stocké sur le serveur Redis. Dans la ligne de commande Redis, entrez les keys * commande, qui renvoie toutes les clés correspondant à n'importe quel modèle. Vous devriez voir une clé appelée "example:1:views.decorators.cache.cache_page". N'oubliez pas que "exemple" est notre préfixe de clé, "1" est la version et "views.decorators.cache.cache_page" est le nom que Django donne à la clé. Copiez le nom de la clé et saisissez-le avec get commande. Vous devriez voir la chaîne HTML rendue.

$ redis-cli -n 1
127.0.0.1:6379[1]> keys *
1) "example:1:views.decorators.cache.cache_header"
2) "example:1:views.decorators.cache.cache_page"
127.0.0.1:6379[1]> get "example:1:views.decorators.cache.cache_page"

REMARQUE : Exécutez le flushall sur la CLI Redis pour effacer toutes les clés du magasin de données. Ensuite, vous pouvez réexécuter les étapes de ce didacticiel sans avoir à attendre que le cache expire.




Récapitulatif

Le traitement des requêtes HTTP est coûteux, et ce coût augmente à mesure que votre application gagne en popularité. Dans certains cas, vous pouvez réduire considérablement la quantité de traitement effectuée par votre serveur en implémentant la mise en cache. Ce tutoriel a abordé les bases de la mise en cache dans Django avec Redis, mais il n'a fait qu'effleurer la surface d'un sujet complexe.

L'implémentation de la mise en cache dans une application robuste présente de nombreux pièges et pièges. Contrôler ce qui est mis en cache et pendant combien de temps est difficile. L'invalidation du cache est l'une des choses les plus difficiles en informatique. S'assurer que les données privées ne sont accessibles qu'aux utilisateurs auxquels elles sont destinées est un problème de sécurité et doit être manipulé avec beaucoup de soin lors de la mise en cache.

Bonus gratuit : Cliquez ici pour accéder à un guide gratuit des ressources d'apprentissage Django (PDF) qui vous montre des trucs et astuces ainsi que les pièges courants à éviter lors de la création d'applications Web Python + Django.

Jouez avec le code source dans l'exemple d'application et pendant que vous continuez à développer avec Django, n'oubliez pas de toujours garder les performances à l'esprit.