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
- Ü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.
- 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?
- Tauchen Sie in das offizielle Celery-Benutzerhandbuch ein, um mehr zu erfahren.
- Erstellen Sie eine Fabfile, um Supervisor und die Konfigurationsdateien einzurichten. Achten Sie darauf, die Befehle zu
reread
hinzuzufügen undupdate
Betreuer. - 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!