Les applications Web sont généralement simples au début, mais peuvent devenir assez complexes et la plupart d'entre elles dépassent rapidement la responsabilité de répondre aux requêtes HTTP..
Lorsque cela se produit, il faut faire la distinction entre ce qui doit se passer instantanément (généralement dans le cycle de vie de la requête HTTP) et ce qui peut éventuellement arriver. Pourquoi donc? Eh bien, parce que lorsque votre application est surchargée de trafic, des choses simples comme celle-ci font la différence.
Les opérations dans une application Web peuvent être classées comme des opérations critiques ou à la demande et des tâches en arrière-plan, celles qui se produisent en dehors du temps de demande. Ceux-ci correspondent à ceux décrits ci-dessus:
Les opérations à la demande peuvent être effectuées sur un seul cycle de demande / réponse sans craindre que le délai d'attente de l'opération ne soit dépassé ou que l'utilisateur risque d'avoir une mauvaise expérience. Les exemples courants incluent les opérations de base de données CRUD (Créer, Lire, Mettre à jour, Supprimer) et la gestion des utilisateurs (routines de connexion / déconnexion)..
Les tâches en arrière-plan sont différentes car elles prennent généralement beaucoup de temps et sont sujettes à l'échec, principalement en raison de dépendances externes. Parmi les scénarios courants parmi les applications Web complexes, citons:
Les tâches d’arrière-plan sont l’objet principal de ce tutoriel. Le modèle de programmation le plus couramment utilisé pour ce scénario est l’architecture grand public du producteur..
En termes simples, cette architecture peut être décrite ainsi:
Habituellement, les consommateurs extraient les tâches de la file d'attente selon le principe premier entré premier sorti (FIFO) ou selon leurs priorités. Les consommateurs sont également appelés travailleurs, et c'est le terme que nous utiliserons tout au long, car il est cohérent avec la terminologie utilisée par les technologies abordées..
Quels types de tâches peuvent être traitées en arrière-plan? Des tâches qui:
Le céleri est le choix de facto pour le traitement des tâches en arrière-plan dans l'écosystème Python / Django. Il possède une API simple et claire, et s’intègre parfaitement à Django. Il prend en charge diverses technologies pour la file d'attente des tâches et divers paradigmes pour les travailleurs..
Dans ce didacticiel, nous allons créer une application Web Django (prenant en charge des scénarios réels) utilisant le traitement de tâches en arrière-plan..
En supposant que vous soyez déjà familiarisé avec la gestion de paquets Python et les environnements virtuels, installons Django:
$ pip installer Django
J'ai décidé de créer une autre application de blogging. L'application sera axée sur la simplicité. Un utilisateur peut simplement créer un compte et sans trop de problèmes créer un message et le publier sur la plateforme..
Mettre en place le quick_publisher
Projet Django:
$ django-admin startproject quick_publisher
Commençons l'application:
$ cd quick_publisher $ ./manage.py startapp main
Lors du démarrage d’un nouveau projet Django, j’aime créer un principale
application qui contient, entre autres, un modèle utilisateur personnalisé. Le plus souvent, je rencontre des limitations du Django par défaut Utilisateur
modèle. Avoir une coutume Utilisateur
le modèle nous donne l'avantage de la flexibilité.
# main / models.py from django.db importer des modèles de django.contrib.auth.models import AbstractBaseUser, PermissionsMixin, BaseUserManager, classe UserAccountManager (BaseUserManager): use_in_migrations = True def _create_user (auto, email, mot de passe, ** extra_fields): if not email: raise ValueError ('Une adresse email doit être fournie') sinon un mot de passe: raise ValueError ('un mot de passe doit être fourni') email = self.normalize_email (email) user = self.model (email = email, ** extra_fields) user.set_password (mot de passe) user.save (using = self._db) retourne def utilisateur create_user (self, email = None, mot de passe = None, ** extra_fields): retourne self._create_user (email, mot de passe, ** extra_fields) def create_superuser (self, email, password, ** extra_fields): extra_fields ['is_staff'] = True extra_fields ['is_superuser'] = True return self._create_user (email, mot de passe, ** extra_fields), classe User (AbstractBaseUser, PermissionsMixin): REQUIRED_FIELDS = [] USERNAME_FIELD = "objets de messagerie" = UserAccountManager () email = models.EmailField ('email', unique = True, vide = False, null = False) nom_complet = models.CharField ('nom complet', blank = Vrai, null = True, longueur_max = 400) is_staff = models.BooleanField ('statut du personnel' , default = False) is_active = models.BooleanField ('active', default = True) def get_short_name (self): retourne self.email def get_full_name (self): retourne self.email def __unicode __ (self): retournera self.email
Assurez-vous de consulter la documentation de Django si vous n'êtes pas familiarisé avec le fonctionnement des modèles utilisateur personnalisés..
Nous devons maintenant dire à Django d’utiliser ce modèle d’utilisateur plutôt que le modèle par défaut. Ajouter cette ligne au quick_publisher / settings.py
fichier:
AUTH_USER_MODEL = 'main.User'
Nous devons également ajouter le principale
application au INSTALLED_APPS
liste dans le quick_publisher / settings.py
fichier. Nous pouvons maintenant créer les migrations, les appliquer et créer un superutilisateur pour pouvoir se connecter au panneau d'administration Django:
$ ./manage.py makemigrations main $ ./manage.py migrate $ ./manage.py createuperuser
Créons maintenant une application Django distincte responsable des publications:
$ ./manage.py startapp publish
Définissons un modèle simple post dans éditeur / modèles.py
:
from django.db modèles d'importation de django.utils importez le fuseau horaire de django.contrib.auth import get_user_model, classe Post (models.Model): author = models.ForeignKey (get_user_model ()) created = models.DateTimeField ('Created Date', default = timezone.now) title = models.CharField ('Title', longueur_max = 200) content = models.TextField ('Content') slug = models.SlugField ('Slug') def __str __ (auto): retour '"% s "par% s '% (self.title, self.author)
Accrocher le Poster
modèle avec l'administrateur Django est fait dans le publisher / admin.py
fichier comme ceci:
depuis django.contrib import admin depuis .models import Post @ admin.register (Post) classe PostAdmin (admin.ModelAdmin): pass
Enfin, accrochons le éditeur
application à notre projet en l'ajoutant à la INSTALLED_APPS
liste.
Nous pouvons maintenant lancer le serveur et nous diriger vers http: // localhost: 8000 / admin /
et créer nos premiers posts pour que nous puissions jouer avec:
$ ./manage.py runserver
J'espère que vous avez fait vos devoirs et que vous avez créé les posts.
Allons-nous en. La prochaine étape évidente consiste à créer un moyen de visualiser les messages publiés..
# publisher / views.py from import django.http Http404 from import django.shortcuts rend le rendu depuis .models import Post def view_post (demande, slug): try: post = Post.objects.get (slug = slug) sauf Post.DoesNotExist: raise Http404 ("Le sondage n'existe pas") renvoie le rendu (request, 'post.html', context = 'post': post)
Associons notre nouvelle vue à une URL dans: quick_publisher / urls.py
# quick_publisher / urls.py de django.conf.urls import url de django.contrib import admin de publisher.views import view_post urlpatterns = [url (r '^ admin /', admin.site.urls), url (r '^ (? P[a-zA-Z0-9 \ -] +) ', view_post, name = "view_post")]
Enfin, créons le modèle qui rend la publication dans: publisher / templates / post.html
titre de l'article
Publier un contenu
Publié par post.author.full_name sur post.created
Nous pouvons maintenant aller sur http: // localhost: 8000 / the-slug-of-the-post-you-created / dans le navigateur. Ce n’est pas vraiment un miracle de la conception Web, mais faire de beaux messages dépasse le cadre de ce tutoriel..
Voici le scénario classique:
Ajoutons un is_verified
drapeau et le verification_uuid
sur le Utilisateur
modèle:
# main / models.py import uuid class User (AbstractBaseUser, PermissionsMixin): REQUIRED_FIELDS = [] USERNAME_FIELD = 'email' objects = UserAccountManager () email = models.EmailField ('email', unique = Vrai, vide, Vrai = False, null = False) full_name = models.CharField ('nom complet', blank = True, null = True, longueur_max = 400) is_staff = models.BooleanField ('statut du personnel', défaut = False) is_active = models.BooleanField ('actif', default = True) is_verified = models.BooleanField ('vérifié', default = False) # Ajoute l'indicateur 'is_verified' verification verification_uuid = models.UUIDField ('Unique Verification UUID', default = uuid.uuid4) def get_short_name (self): return self.email def get_full_name (self): retourne self.email def __unicode __ (self): retour self.email
Profitons de cette occasion pour ajouter le modèle d’utilisateur à l’administrateur:
depuis django.contrib import admin de .models import User @ admin.register (User), classe UserAdmin (admin.ModelAdmin): pass
Faisons en sorte que les modifications soient reflétées dans la base de données:
$ ./manage.py makemigrations $ ./manage.py migrate
Nous devons maintenant écrire un morceau de code qui envoie un courrier électronique lors de la création d'une instance d'utilisateur. C’est ce à quoi servent les signaux Django, et c’est l’occasion parfaite pour aborder ce sujet..
Les signaux sont déclenchés avant / après que certains événements se produisent dans l'application. Nous pouvons définir des fonctions de rappel déclenchées automatiquement lorsque les signaux sont déclenchés. Pour déclencher un rappel, nous devons d’abord le connecter à un signal.
Nous allons créer un rappel qui sera déclenché après la création d'un modèle utilisateur. Nous ajouterons ce code après la Utilisateur
définition du modèle en: main / models.py
depuis django.db.models, importez les signaux provenant de django.core.mail import send_mail def user_post_save (expéditeur, instance, signal, * arguments, ** kwargs): si ce n'est pas instance.is_verified: # Envoi du courrier électronique de vérification send_mail ('Vérifiez votre compte QuickPublisher Suivez ce lien pour vérifier votre compte: "http: // localhost: 8000% s"% reverse ('verify', kwargs = 'uuid': str (instance.verification_uuid)) ', de @ quickpublisher. dev ', [instance.email], fail_silently = False,) signaux.post_save.connect (user_post_save, sender = utilisateur)
Ce que nous avons fait ici est que nous avons défini un user_post_save
fonction et connecté à la post_save
signal (déclenché après la sauvegarde d’un modèle) envoyé par le Utilisateur
modèle.
Django n'envoie pas uniquement des courriels; il doit être lié à un service de messagerie. Par souci de simplicité, vous pouvez ajouter vos informations d'identification Gmail dans quick_publisher / settings.py
, ou vous pouvez ajouter votre fournisseur de messagerie préféré.
Voici à quoi ressemble la configuration de Gmail:
EMAIL_USE_TLS = True EMAIL_HOST = 'smtp.gmail.com' EMAIL_HOST_USER = '@ gmail.com 'EMAIL_HOST_PASSWORD =' 'EMAIL_PORT = 587
Pour tester tout cela, allez dans le panneau d’administration et créez un nouvel utilisateur avec une adresse email valide que vous pourrez vérifier rapidement. Si tout va bien, vous recevrez un email avec un lien de vérification. La routine de vérification n'est pas encore prête.
Voici comment vérifier le compte:
# main / views.py from import django.http Http404 depuis django.shortcuts import render, redirect depuis .models import Utilisateur def home (demande): retour rend (demande, 'home.html') def verify (demande, uuid): try: user = User.objects.get (verification_uuid = uuid, is_verified = False) sauf User.DoesNotExist: raise Http404 ("L'utilisateur n'existe pas ou est déjà vérifié") user.is_verified = True user.save () return redirect ( 'maison')
Accrochez les vues dans: quick_publisher / urls.py
# quick_publisher / urls.py de django.conf.urls import url de django.contrib import admin de publisher.views import view_post de main.views import home, vérifiez urlpatterns = [url (r '^ $', home, name = " home "), url (r '^ admin /', admin.site.urls), url (r '^ verify / (? P[a-z0-9 \ -] +) / ', vérifier, nom = "vérifier"), url (r' ^ (? P [a-zA-Z0-9 \ -] +) ', view_post, name = "view_post")]
En outre, n'oubliez pas de créer un home.html
déposer sous main / templates / home.html
. Il sera rendu par le maison
vue.
Essayez de recommencer tout le scénario. Si tout va bien, vous recevrez un email avec une URL de vérification valide. Si vous suivez l'URL, puis vérifiez dans l'administrateur, vous pouvez voir comment le compte a été vérifié..
Voici le problème avec ce que nous avons fait jusqu'à présent. Vous avez peut-être remarqué que créer un utilisateur est un peu lent. En effet, Django envoie l'e-mail de vérification dans le délai imparti..
Voici comment cela fonctionne: nous envoyons les données utilisateur à l'application Django. L'application crée un Utilisateur
modèle, puis crée une connexion à Gmail (ou à un autre service que vous avez sélectionné). Django attend la réponse, puis renvoie une réponse à notre navigateur..
Voici où le céleri entre en jeu. Tout d’abord, assurez-vous qu’il est installé:
$ pip installer le céleri
Nous devons maintenant créer une application de céleri dans notre application Django:
# quick_publisher / celery.py import de celery import Celery os.environ.setdefault ('DJANGO_SETTINGS_MODULE', 'quick_publisher.settings') app = Celery ('quick_publisher') app.config_from_object ('django.conf: settings') modules de tâches de toutes les configurations d’application Django enregistrées. app.autodiscover_tasks ()
Le céleri est une file d'attente de tâches. Il reçoit des tâches de notre application Django et les exécute en arrière-plan. Le céleri doit être associé à d’autres services agissant en tant que courtiers.
Les courtiers interviennent dans l'envoi de messages entre l'application Web et le céleri. Dans ce tutoriel, nous utiliserons Redis. Redis est facile à installer et nous pouvons facilement commencer à l'utiliser sans faire d'histoires..
Vous pouvez installer Redis en suivant les instructions de la page Redis Quick Start. Vous devrez installer la bibliothèque Redis Python, pip installer redis
, et le paquet nécessaire à l’utilisation de Redis et Céleri: pip installer céleri [redis]
.
Démarrez le serveur Redis dans une console séparée, comme ceci: $ redis-server
Ajoutons les configurations relatives au céleri / Redis dans quick_publisher / settings.py
:
# Paramètres associés à REDIS REDIS_HOST = 'localhost' REDIS_PORT = '6379' BROKER_URL = 'redis: //' + REDIS_HOST + ':' + REDIS_PORT + '/ 0' BROKER_TRANSPORT_OPTIONS = 'accessibility_timeout': 3600 CELERY_RESULT_BACKEND / '+ REDIS_HOST +': '+ REDIS_PORT +' / 0 '
Avant de pouvoir exécuter quoi que ce soit dans le céleri, il faut le déclarer comme tâche.
Voici comment faire ceci:
# main / tasks.py importation journalisation à partir de django.urls importation inversée à partir de django.core.mail importation send_mail à partir de django.contrib.auth importation get_user_model à partir de quick_publisher.celery import app @ app.task def send_verification_email (user_id): UserModel = get_user_model ( ) try: user = UserModel.objects.get (pk = user_id) send_mail ('Vérifiez votre compte QuickPublisher', 'Suivez ce lien pour vérifier votre compte: "http: // localhost: 8000% s'% reverse ('verify' , kwargs = 'uuid': str (user.verification_uuid)), '[email protected]', [user.email], fail_silently = False,) sauf UserModel.DoesNotExist: logging.warning ("Tentative d'envoyer la vérification e-mail à l'utilisateur non existant '% s' "% user_id)
Voici ce que nous avons fait ici: nous avons déplacé la fonctionnalité d’envoi du courrier électronique de vérification dans un autre fichier appelé tâches.py
.
Quelques notes:
INSTALLED_APPS
et enregistre les tâches dans tâches.py
des dossiers.Envoyer email de vérification
fonctionner avec @ app.task
. Cela indique à Celery que cette tâche sera exécutée dans la file d'attente..identifiant d'utilisateur
Plutôt qu'un Utilisateur
objet. En effet, nous pourrions avoir des difficultés à sérialiser des objets complexes lors de l'envoi des tâches à Celery. Il est préférable de les garder simples.Revenir à main / models.py
, le code du signal devient:
depuis django.db.models signaux d'importation depuis main.tasks import send_verification_email def user_post_save (expéditeur, instance, signal, * args, ** kwargs): si ce n'est pas instance.is_verified: # Envoie un courrier électronique de vérification send_verification_email.delay (instance.pk) .post_save.connect (user_post_save, sender = User)
Remarquez comment nous appelons le .retard
méthode sur l'objet de tâche. Cela signifie que nous envoyons la tâche à Celery et que nous n'attendons pas le résultat. Si on utilisait send_verification_email (instance.pk)
au lieu de cela, nous l'enverrions toujours à Celery, mais nous attendrions la fin de la tâche, ce qui n'est pas ce que nous souhaitons.
Avant de créer un nouvel utilisateur, il y a un problème. Le céleri est un service et nous devons le démarrer. Ouvrez une nouvelle console, assurez-vous d'activer le fichier virtualenv approprié et accédez au dossier du projet..
$ celery worker -A quick_publisher --loglevel = debug --concurrency = 4
Cela démarre quatre travailleurs du processus de céleri. Oui, vous pouvez enfin créer un autre utilisateur. Remarquez qu'il n'y a pas de délai et surveillez les journaux de la console Celery pour voir si les tâches sont correctement exécutées. Cela devrait ressembler à ceci:
[2017-04-28 15: 00: 09,190: DEBUG / MainProcess] Tâche acceptée: main.tasks.send_verification_email [f1f41e1f-ca39-43d2-a37d-9de085dc99de] pid: 62065 [2017-04-28 15: 00: 11.740: INFO / PoolWorker-2] La tâche main.tasks.send_verification_email [f1f41e1f-ca39-43d2-a37d-9de085dc99de] a abouti sur la valeur 2.5500912349671125s: Aucune
Voici un autre scénario courant. La plupart des applications Web matures envoient aux utilisateurs des courriers électroniques sur leur cycle de vie afin de les maintenir engagés. Quelques exemples courants d'e-mails de cycle de vie:
Voici ce que nous allons faire dans notre application. Nous allons compter le nombre de fois que chaque publication a été vue et envoyer un rapport quotidien à l'auteur. Une fois par jour, nous allons passer en revue tous les utilisateurs, récupérer leurs publications et envoyer un courrier électronique avec un tableau contenant les publications et afficher le nombre de vues..
Changeons le Poster
modèle afin que nous puissions accueillir le scénario compte points de vue.
classe Post (models.Model): author = models.ForeignKey (Utilisateur) created = models.DateTimeField ('Date de création', default = timezone.now) title = models.CharField ('Title', max_length = 200) content = models .TextField ('Contenu') slug = models.SlugField ('Slug') view_count = models.IntegerField ("Nombre de vues", default = 0) def __str __ (self): retourne ""% s "par% s"% ( self.title, self.author)
Comme toujours, lorsque nous modifions un modèle, nous devons migrer la base de données:
$ ./manage.py makemigrations $ ./manage.py migrate
Modifions également le voir l'article
Django view pour compter les vues:
def view_post (request, slug): try: post = post.objects.get (slug = slug) sauf Post.DoesNotExist: raise Http404 ("Le sondage n'existe pas") post.view_count + = 1 post.save () return render (request, 'post.html', context = 'post': post)
Il serait utile d'afficher le Consulté post.view_count foisnombre de vues
dans le modèle. Ajoute ça
quelque part à l'intérieur du publisher / templates / post.html
fichier. Faire quelques vues sur un post maintenant et voir comment le compteur augmente.
Créons une tâche de céleri. Puisqu'il s'agit de posts, je vais le placer dans publisher / tasks.py
:
from django.template import Template, Contexte de django.core.mail import send_mail de django.contrib.auth import get_user_model à partir de quick_publisher.celery application d'importation publisher.models import Post REPORT_TEMPLATE = "" "Voici comment vous vous en êtes rendu: % pour les messages dans les messages% "post.title": visualisé post.view_count fois | % endfor% "" "@ app.task def send_view_count_report (): pour l'utilisateur dans get_user_model (). objects.all (): posts = Post.objects.filter (auteur = utilisateur) si non posts: continue template = Modèle (REPORT_TEMPLATE) send_mail ('Votre activité QuickPublisher', template.render (context = Contexte ('posts': posts)), '[email protected]', [user.email], fail_silently = False,)
N'oubliez pas de redémarrer le processus de céleri chaque fois que vous apportez des modifications aux tâches de céleri. Le céleri a besoin de découvrir et de recharger des tâches. Avant de créer une tâche périodique, testez-la dans le shell Django pour vous assurer que tout fonctionne comme prévu:
shell $ ./manage.py Dans [1]: à partir de publisher.tasks import send_view_count_report Dans [2]: send_view_count_report.delay ()
J'espère que vous avez reçu un joli petit rapport dans votre email.
Créons maintenant une tâche périodique. S'ouvrir quick_publisher / celery.py
et enregistrez les tâches périodiques:
# quick_publisher / celery.py import de celery import Céleri de celery.schedules import crontab os.environ.setdefault ('DJANGO_SETTINGS_MODULE', 'quick_publisher.settings') app = Celery ('quick_publisher') app.config_from_object ('django> : settings ') # Charge les modules de tâches à partir de toutes les configurations d’application Django enregistrées. app.autodiscover_tasks () app.conf.beat_schedule = 'envoyer un rapport à chaque minute': 'tâche': 'publisher.tasks.send_view_count_report', 'schedule': crontab (), # changer en 'crontab (minute = 0, hour = 0) 'si vous voulez qu'il fonctionne tous les jours à minuit,
Jusqu'à présent, nous avons créé un calendrier qui exécuterait la tâche publisher.tasks.send_view_count_report
chaque minute comme indiqué par le crontab ()
notation. Vous pouvez également spécifier divers calendriers Celery Crontab.
Ouvrez une autre console, activez l'environnement approprié et démarrez le service Celery Beat.
$ celery -Un battement de quick_publisher
Le travail du service Beat consiste à pousser les tâches dans le céleri conformément au calendrier. Tenez compte du fait que le calendrier rend la send_view_count_report
tâche exécutée chaque minute en fonction de la configuration. C'est bon pour les tests, mais pas recommandé pour une application Web réelle.
Les tâches sont souvent utilisées pour effectuer des opérations non fiables, des opérations qui dépendent de ressources externes ou qui peuvent facilement échouer pour diverses raisons. Voici un guide pour les rendre plus fiables:
J'espère que cela a été un tutoriel intéressant pour vous et une bonne introduction à l'utilisation du céleri avec Django.
Voici quelques conclusions que nous pouvons tirer:
battement de céleri
un service.