Database
 sql >> Datenbank >  >> RDS >> Database

Asynchrone Aufgaben mit Django und Sellerie

Als ich neu bei Django war, war eines der frustrierendsten Dinge, die ich erlebte, die Notwendigkeit, regelmäßig ein bisschen Code auszuführen. Ich habe eine nette Funktion geschrieben, die eine Aktion ausführt, die täglich um 00:00 Uhr ausgeführt werden muss. Einfach richtig? Falsch. Dies stellte sich für mich als großes Problem heraus, da ich zu der Zeit an Webhosting vom Typ „Cpanel“ gewöhnt war, wo es eine nette praktische GUI zum Einrichten von Cron-Jobs für genau diesen Zweck gab.

Nach langer Recherche fand ich eine nette Lösung – Sellerie, eine leistungsstarke asynchrone Auftragswarteschlange, die zum Ausführen von Aufgaben im Hintergrund verwendet wird. Dies führte jedoch zu zusätzlichen Problemen, da ich keine einfache Anleitung finden konnte, um Celery in ein Django-Projekt zu integrieren.

Natürlich habe ich es schließlich geschafft, es herauszufinden – und darum geht es in diesem Artikel:Wie man Sellerie in ein Django-Projekt integriert und regelmäßige Aufgaben erstellt.

Kostenloser Bonus: Klicken Sie hier, um Zugang zu einem kostenlosen Django Learning Resources Guide (PDF) zu erhalten, der Ihnen Tipps und Tricks sowie häufige Fallstricke zeigt, die Sie beim Erstellen von Python + Django-Webanwendungen vermeiden sollten.

Dieses Projekt verwendet Python 3.4, Django 1.8.2, Celery 3.1.18 und Redis 3.0.2.


Übersicht

Da dies ein so umfangreicher Beitrag ist, finden Sie zu Ihrer Bequemlichkeit kurze Informationen zu jedem Schritt und den zugehörigen Code in dieser Tabelle.

Schritt Übersicht Git-Tag
Boilerplate Vorlage herunterladen v1
Einrichtung Integrieren Sie Sellerie in Django v2
Sellerie-Aufgaben Einfache Sellerie-Aufgabe hinzufügen v3
Periodische Aufgaben Periodische Aufgabe hinzufügen v4
Lokal ausgeführt Führen Sie unsere App lokal aus v5
Aus der Ferne ausführen Führen Sie unsere App remote aus v6


Was ist Sellerie?

„Celery ist eine asynchrone Aufgabenwarteschlange/Auftragswarteschlange, die auf verteilter Nachrichtenweitergabe basiert. Es konzentriert sich auf den Echtzeitbetrieb, unterstützt aber auch die Zeitplanung.“ In diesem Beitrag konzentrieren wir uns auf die Planungsfunktion, um einen Job/eine Aufgabe regelmäßig auszuführen.

Warum ist das nützlich?

  • Denken Sie an all die Zeiten, in denen Sie in Zukunft eine bestimmte Aufgabe ausführen mussten. Vielleicht mussten Sie jede Stunde auf eine API zugreifen. Oder vielleicht mussten Sie am Ende des Tages eine Reihe von E-Mails versenden. Ob groß oder klein, Celery macht die Planung solcher regelmäßigen Aufgaben einfach.
  • Sie möchten niemals, dass Endbenutzer unnötig warten müssen, bis Seiten geladen oder Aktionen abgeschlossen sind. Wenn ein langer Prozess Teil des Workflows Ihrer Anwendung ist, können Sie Celery verwenden, um diesen Prozess im Hintergrund auszuführen, sobald Ressourcen verfügbar werden, sodass Ihre Anwendung weiterhin auf Clientanforderungen reagieren kann. Dadurch wird die Aufgabe aus dem Kontext der Anwendung herausgehalten.


Einrichtung

Bevor Sie in Celery eintauchen, holen Sie sich das Starter-Projekt aus dem Github-Repo. Stellen Sie sicher, dass Sie eine virtuelle Umgebung aktivieren, die Anforderungen installieren und die Migrationen ausführen. Starten Sie dann den Server und navigieren Sie in Ihrem Browser zu http://localhost:8000/. Sie sollten den vertrauten Text „Herzlichen Glückwunsch zu Ihrer ersten Django-betriebenen Seite“ sehen. Wenn Sie fertig sind, beenden Sie den Server.

Als nächstes installieren wir Celery mit pip:

$ pip install celery==3.1.18
$ pip freeze > requirements.txt

Jetzt können wir Celery in nur drei einfachen Schritten in unser Django-Projekt integrieren.


Schritt 1:Fügen Sie celery.py hinzu

Erstellen Sie im Verzeichnis „picha“ eine neue Datei mit dem Namen celery.py :

from __future__ import absolute_import
import os
from celery import Celery
from django.conf import settings

# set the default Django settings module for the 'celery' program.
os.environ.setdefault('DJANGO_SETTINGS_MODULE', 'picha.settings')
app = Celery('picha')

# Using a string here means the worker will not have to
# pickle the object when using Windows.
app.config_from_object('django.conf:settings')
app.autodiscover_tasks(lambda: settings.INSTALLED_APPS)


@app.task(bind=True)
def debug_task(self):
    print('Request: {0!r}'.format(self.request))

Beachten Sie die Kommentare im Code.



Schritt 2:Importieren Sie Ihre neue Sellerie-App

Um sicherzustellen, dass die Celery-App beim Start von Django geladen wird, fügen Sie den folgenden Code in __init__.py ein Datei, die sich neben Ihrer settings.py befindet Datei:

from __future__ import absolute_import

# This will make sure the app is always imported when
# Django starts so that shared_task will use this app.
from .celery import app as celery_app

Danach sollte Ihr Projektlayout nun so aussehen:

├── manage.py
├── picha
│   ├── __init__.py
│   ├── celery.py
│   ├── settings.py
│   ├── urls.py
│   └── wsgi.py
└── requirements.txt


Schritt 3:Installieren Sie Redis als Sellerie-„Broker“

Celery verwendet „Broker“, um Nachrichten zwischen einem Django-Projekt und den Celery-Workern weiterzuleiten. In diesem Tutorial verwenden wir Redis als Message Broker.

Installieren Sie zuerst Redis von der offiziellen Downloadseite oder über brew (brew install redis ) und wenden Sie sich dann Ihrem Terminal zu, starten Sie in einem neuen Terminalfenster den Server:

$ redis-server

Sie können testen, ob Redis ordnungsgemäß funktioniert, indem Sie Folgendes in Ihr Terminal eingeben:

$ redis-cli ping

Redis sollte mit PONG antworten - Probieren Sie es aus!

Sobald Redis hochgefahren ist, fügen Sie Ihrer settings.py-Datei den folgenden Code hinzu:

# CELERY STUFF
BROKER_URL = 'redis://localhost:6379'
CELERY_RESULT_BACKEND = 'redis://localhost:6379'
CELERY_ACCEPT_CONTENT = ['application/json']
CELERY_TASK_SERIALIZER = 'json'
CELERY_RESULT_SERIALIZER = 'json'
CELERY_TIMEZONE = 'Africa/Nairobi'

Sie müssen Redis auch als Abhängigkeit im Django-Projekt hinzufügen:

$ pip install redis==2.10.3
$ pip freeze > requirements.txt

Das ist es! Sie sollten Celery jetzt mit Django verwenden können. Weitere Informationen zum Einrichten von Celery mit Django finden Sie in der offiziellen Celery-Dokumentation.

Bevor wir fortfahren, führen wir ein paar Plausibilitätsprüfungen durch, um sicherzustellen, dass alles in Ordnung ist …

Testen Sie, ob der Celery-Arbeiter bereit ist, Aufgaben zu empfangen:

$ celery -A picha worker -l info
...
[2015-07-07 14:07:07,398: INFO/MainProcess] Connected to redis://localhost:6379//
[2015-07-07 14:07:07,410: INFO/MainProcess] mingle: searching for neighbors
[2015-07-07 14:07:08,419: INFO/MainProcess] mingle: all alone

Beenden Sie den Prozess mit STRG-C. Testen Sie nun, ob der Aufgabenplaner von Celery einsatzbereit ist:

$ celery -A picha beat -l info
...
[2015-07-07 14:08:23,054: INFO/MainProcess] beat: Starting...

Boom!

Beenden Sie den Prozess erneut, wenn Sie fertig sind.




Sellerie-Aufgaben

Celery verwendet Tasks, die man sich als reguläre Python-Funktionen vorstellen kann, die mit Celery aufgerufen werden.

Lassen Sie uns diese Grundfunktion zum Beispiel in eine Celery-Aufgabe umwandeln:

def add(x, y):
    return x + y

Fügen Sie zuerst einen Decorator hinzu:

from celery.decorators import task

@task(name="sum_two_numbers")
def add(x, y):
    return x + y

Dann können Sie diese Aufgabe asynchron mit Sellerie wie folgt ausführen:

add.delay(7, 8)

Einfach, oder?

Diese Arten von Aufgaben sind also perfekt, wenn Sie eine Webseite laden möchten, ohne dass der Benutzer auf den Abschluss eines Hintergrundprozesses warten muss.

Sehen wir uns ein Beispiel an…

Um auf das Django-Projekt zurückzukommen, nehmen Sie Version drei, die eine App enthält, die Feedback von Benutzern akzeptiert, treffend als feedback bezeichnet :

├── feedback
│   ├── __init__.py
│   ├── admin.py
│   ├── emails.py
│   ├── forms.py
│   ├── models.py
│   ├── tests.py
│   └── views.py
├── manage.py
├── picha
│   ├── __init__.py
│   ├── celery.py
│   ├── settings.py
│   ├── urls.py
│   └── wsgi.py
├── requirements.txt
└── templates
    ├── base.html
    └── feedback
        ├── contact.html
        └── email
            ├── feedback_email_body.txt
            └── feedback_email_subject.txt

Installieren Sie die neuen Anforderungen, starten Sie die App und navigieren Sie zu http://localhost:8000/feedback/. Sie sollten sehen:

Lassen Sie uns die Celery-Aufgabe verdrahten.


Aufgabe hinzufügen

Grundsätzlich möchten wir, nachdem der Benutzer das Feedback-Formular abgeschickt hat, ihn sofort seinen fröhlichen Weg fortsetzen lassen, während wir das Feedback bearbeiten, eine E-Mail senden usw., alles im Hintergrund.

Fügen Sie dazu zunächst eine Datei namens tasks.py hinzu in das „Feedback“-Verzeichnis:

from celery.decorators import task
from celery.utils.log import get_task_logger

from feedback.emails import send_feedback_email

logger = get_task_logger(__name__)


@task(name="send_feedback_email_task")
def send_feedback_email_task(email, message):
    """sends an email when feedback form is filled successfully"""
    logger.info("Sent feedback email")
    return send_feedback_email(email, message)

Aktualisieren Sie dann forms.py so:

from django import forms
from feedback.tasks import send_feedback_email_task


class FeedbackForm(forms.Form):
    email = forms.EmailField(label="Email Address")
    message = forms.CharField(
        label="Message", widget=forms.Textarea(attrs={'rows': 5}))
    honeypot = forms.CharField(widget=forms.HiddenInput(), required=False)

    def send_email(self):
        # try to trick spammers by checking whether the honeypot field is
        # filled in; not super complicated/effective but it works
        if self.cleaned_data['honeypot']:
            return False
        send_feedback_email_task.delay(
            self.cleaned_data['email'], self.cleaned_data['message'])

Im Wesentlichen die send_feedback_email_task.delay(email, message) Funktion verarbeitet und sendet die Feedback-E-Mail im Hintergrund, während der Benutzer die Website weiterhin nutzt.

HINWEIS :Die success_url in views.py ist so eingestellt, dass der Benutzer zu / umgeleitet wird , die es noch nicht gibt. Wir werden diesen Endpunkt im nächsten Abschnitt einrichten.




Periodische Aufgaben

Oft müssen Sie eine Aufgabe so planen, dass sie von Zeit zu Zeit zu einer bestimmten Zeit ausgeführt wird – d. H. Ein Web Scraper muss beispielsweise möglicherweise täglich ausgeführt werden. Solche Aufgaben, sogenannte periodische Aufgaben, lassen sich mit Celery einfach einrichten.

Sellerie verwendet „Sellerie-Beat“, um regelmäßige Aufgaben zu planen. Sellerieschlag führt in regelmäßigen Abständen Aufgaben aus, die dann von Selleriearbeitern ausgeführt werden.

Die folgende Aufgabe wird beispielsweise so geplant, dass sie alle 15 Minuten ausgeführt wird:

from celery.task.schedules import crontab
from celery.decorators import periodic_task


@periodic_task(run_every=(crontab(minute='*/15')), name="some_task", ignore_result=True)
def some_task():
    # do something

Sehen wir uns ein robusteres Beispiel an, indem wir diese Funktionalität zum Django-Projekt hinzufügen …

Zurück zum Django-Projekt, holen Sie sich Version vier, die eine weitere neue App namens photos enthält , das die Flickr-API verwendet, um neue Fotos zur Anzeige auf der Website abzurufen:

├── feedback
│   ├── __init__.py
│   ├── admin.py
│   ├── emails.py
│   ├── forms.py
│   ├── models.py
│   ├── tasks.py
│   ├── tests.py
│   └── views.py
├── manage.py
├── photos
│   ├── __init__.py
│   ├── admin.py
│   ├── models.py
│   ├── settings.py
│   ├── tests.py
│   ├── utils.py
│   └── views.py
├── picha
│   ├── __init__.py
│   ├── celery.py
│   ├── settings.py
│   ├── urls.py
│   └── wsgi.py
├── requirements.txt
└── templates
    ├── base.html
    ├── feedback
    │   ├── contact.html
    │   └── email
    │       ├── feedback_email_body.txt
    │       └── feedback_email_subject.txt
    └── photos
        └── photo_list.html

Installieren Sie die neuen Anforderungen, führen Sie die Migrationen aus und starten Sie dann den Server, um sicherzustellen, dass alles in Ordnung ist. Testen Sie das Feedback-Formular erneut. Diesmal sollte es problemlos umgeleitet werden.

Was kommt als nächstes?

Nun, da wir die Flickr-API regelmäßig aufrufen müssten, um unserer Website weitere Fotos hinzuzufügen, können wir eine Celery-Aufgabe hinzufügen.


Aufgabe hinzufügen

Fügen Sie eine tasks.py hinzu zu den photos Anwendung:

from celery.task.schedules import crontab
from celery.decorators import periodic_task
from celery.utils.log import get_task_logger

from photos.utils import save_latest_flickr_image

logger = get_task_logger(__name__)


@periodic_task(
    run_every=(crontab(minute='*/15')),
    name="task_save_latest_flickr_image",
    ignore_result=True
)
def task_save_latest_flickr_image():
    """
    Saves latest image from Flickr
    """
    save_latest_flickr_image()
    logger.info("Saved image from Flickr")

Hier führen wir save_latest_flickr_image() aus Funktion alle 15 Minuten, indem Sie den Funktionsaufruf in eine task verpacken . Die @periodic_task decorator abstrahiert den Code zum Ausführen der Celery-Aufgabe und lässt die tasks.py übrig Datei sauber und einfach zu lesen!




Lokal ausgeführt

Bereit, dieses Ding auszuführen?

Öffnen Sie bei laufender Django-App und Redis zwei neue Terminalfenster/Registerkarten. Navigieren Sie in jedem neuen Fenster zu Ihrem Projektverzeichnis, aktivieren Sie Ihre virtuelle Umgebung und führen Sie dann die folgenden Befehle aus (einen in jedem Fenster):

$ celery -A picha worker -l info
$ celery -A picha beat -l info

Wenn Sie die Seite unter http://127.0.0.1:8000/ besuchen, sollten Sie jetzt ein Bild sehen. Unsere App ruft alle 15 Minuten ein Bild von Flickr ab:

Sehen Sie sich photos/tasks.py an um den Code zu sehen. Wenn Sie auf die Schaltfläche „Feedback“ klicken, können Sie … Feedback senden:

Dies funktioniert über einen Sellerie-Task. Sehen Sie sich feedback/tasks.py an für mehr.

Das ist es, Sie haben das Picha-Projekt am Laufen!

Dies ist gut zum Testen während der lokalen Entwicklung Ihres Django-Projekts, funktioniert aber nicht so gut, wenn Sie es in der Produktion bereitstellen müssen – wie vielleicht auf DigitalOcean. Dafür wird empfohlen, dass Sie den Celery-Worker und -Scheduler im Hintergrund als Daemon mit Supervisor ausführen.



Aus der Ferne ausführen

Die Installation ist einfach. Holen Sie sich Version fünf aus dem Repo (falls Sie es noch nicht haben). Verbinden Sie sich dann per SSH mit Ihrem Remote-Server und führen Sie Folgendes aus:

$ sudo apt-get install supervisor

Anschließend müssen wir Supervisor über unsere Celery-Worker informieren, indem wir Konfigurationsdateien zum Verzeichnis „/etc/supervisor/conf.d/“ auf dem Remote-Server hinzufügen. In unserem Fall benötigen wir zwei solcher Konfigurationsdateien - eine für den Celery-Worker und eine für den Celery-Scheduler.

Erstellen Sie lokal einen Ordner mit dem Namen „Supervisor“ im Projektstammverzeichnis. Fügen Sie dann die folgenden Dateien hinzu…

Sellerie-Arbeiter:picha_celery.conf

; ==================================
;  celery worker supervisor example
; ==================================

; the name of your supervisord program
[program:pichacelery]

; Set full path to celery program if using virtualenv
command=/home/mosh/.virtualenvs/picha/bin/celery worker -A picha --loglevel=INFO

; The directory to your Django project
directory=/home/mosh/sites/picha

; If supervisord is run as the root user, switch users to this UNIX user account
; before doing any processing.
user=mosh

; Supervisor will start as many instances of this program as named by numprocs
numprocs=1

; Put process stdout output in this file
stdout_logfile=/var/log/celery/picha_worker.log

; Put process stderr output in this file
stderr_logfile=/var/log/celery/picha_worker.log

; If true, this program will start automatically when supervisord is started
autostart=true

; May be one of false, unexpected, or true. If false, the process will never
; be autorestarted. If unexpected, the process will be restart when the program
; exits with an exit code that is not one of the exit codes associated with this
; process’ configuration (see exitcodes). If true, the process will be
; unconditionally restarted when it exits, without regard to its exit code.
autorestart=true

; The total number of seconds which the program needs to stay running after
; a startup to consider the start successful.
startsecs=10

; Need to wait for currently executing tasks to finish at shutdown.
; Increase this if you have very long running tasks.
stopwaitsecs = 600

; When resorting to send SIGKILL to the program to terminate it
; send SIGKILL to its whole process group instead,
; taking care of its children as well.
killasgroup=true

; if your broker is supervised, set its priority higher
; so it starts first
priority=998

Sellerie-Scheduler:picha_celerybeat.conf

; ================================
;  celery beat supervisor example
; ================================

; the name of your supervisord program
[program:pichacelerybeat]

; Set full path to celery program if using virtualenv
command=/home/mosh/.virtualenvs/picha/bin/celerybeat -A picha --loglevel=INFO

; The directory to your Django project
directory=/home/mosh/sites/picha

; If supervisord is run as the root user, switch users to this UNIX user account
; before doing any processing.
user=mosh

; Supervisor will start as many instances of this program as named by numprocs
numprocs=1

; Put process stdout output in this file
stdout_logfile=/var/log/celery/picha_beat.log

; Put process stderr output in this file
stderr_logfile=/var/log/celery/picha_beat.log

; If true, this program will start automatically when supervisord is started
autostart=true

; May be one of false, unexpected, or true. If false, the process will never
; be autorestarted. If unexpected, the process will be restart when the program
; exits with an exit code that is not one of the exit codes associated with this
; process’ configuration (see exitcodes). If true, the process will be
; unconditionally restarted when it exits, without regard to its exit code.
autorestart=true

; The total number of seconds which the program needs to stay running after
; a startup to consider the start successful.
startsecs=10

; if your broker is supervised, set its priority higher
; so it starts first
priority=999

Achten Sie darauf, die Pfade in diesen Dateien zu aktualisieren, damit sie mit dem Dateisystem des Remote-Servers übereinstimmen.

Grundsätzlich teilen diese Supervisor-Konfigurationsdateien Supervisor mit, wie unsere „Programme“ (wie sie von Supervisor genannt werden) auszuführen und zu verwalten sind.

In den obigen Beispielen haben wir zwei Supervisor-Programme mit den Namen „picchacelery“ und „picchacelerybeat“ erstellt.

Kopieren Sie diese Dateien nun einfach auf den Remote-Server in das Verzeichnis „/etc/supervisor/conf.d/“.

Wir müssen auch die Protokolldateien erstellen, die in den obigen Skripten auf dem Remote-Server erwähnt werden:

$ touch /var/log/celery/picha_worker.log
$ touch /var/log/celery/picha_beat.log

Führen Sie schließlich die folgenden Befehle aus, um Supervisor auf die Programme aufmerksam zu machen – z. B. pichacelery und pichacelerybeat :

$ sudo supervisorctl reread
$ sudo supervisorctl update

Führen Sie die folgenden Befehle aus, um pichacelery zu stoppen, zu starten und/oder den Status zu überprüfen Programm:

$ sudo supervisorctl stop pichacelery
$ sudo supervisorctl start pichacelery
$ sudo supervisorctl status pichacelery

Weitere Informationen zu Supervisor finden Sie in der offiziellen Dokumentation.



Letzte Tipps

  1. Übergeben Sie keine Django-Modellobjekte an Celery-Aufgaben. Um Fälle zu vermeiden, in denen sich das Modellobjekt bereits geändert hat, bevor es an eine Celery-Aufgabe übergeben wird, übergeben Sie den Primärschlüssel des Objekts an Celery. Sie müssten dann natürlich den Primärschlüssel verwenden, um das Objekt aus der Datenbank zu holen, bevor Sie daran arbeiten können.
  2. Der standardmäßige Celery-Scheduler erstellt einige Dateien, um seinen Zeitplan lokal zu speichern. Diese Dateien wären „celerybeat-schedule.db“ und „celerybeat.pid“. Wenn Sie ein Versionskontrollsystem wie Git verwenden (was Sie tun sollten!), ist es eine gute Idee, diese Dateien zu ignorieren und sie nicht zu Ihrem Repository hinzuzufügen, da sie dazu dienen, Prozesse lokal auszuführen.


Nächste Schritte

Nun, das war es für die grundlegende Einführung in die Integration von Celery in ein Django-Projekt.

Willst du mehr?

  1. Tauchen Sie in das offizielle Celery-Benutzerhandbuch ein, um mehr zu erfahren.
  2. Erstellen Sie eine Fabfile, um Supervisor und die Konfigurationsdateien einzurichten. Achten Sie darauf, die Befehle zu reread hinzuzufügen und update Betreuer.
  3. Verzweigen Sie das Projekt aus dem Repository und öffnen Sie eine Pull-Anforderung, um eine neue Celery-Aufgabe hinzuzufügen.

Kostenloser Bonus: Klicken Sie hier, um Zugang zu einem kostenlosen Django Learning Resources Guide (PDF) zu erhalten, der Ihnen Tipps und Tricks sowie häufige Fallstricke zeigt, die Sie beim Erstellen von Python + Django-Webanwendungen vermeiden sollten.

Viel Spaß beim Programmieren!