Database
 sql >> Baza danych >  >> RDS >> Database

Zadania asynchroniczne z Django i Celery

Kiedy byłem nowy w Django, jedną z najbardziej frustrujących rzeczy, jakich doświadczyłem, była potrzeba okresowego uruchamiania kodu. Napisałem fajną funkcję, która wykonywała akcję, która musiała być uruchamiana codziennie o 12 rano. Łatwe, prawda? Zło. Okazało się to dla mnie ogromnym problemem, ponieważ w tamtym czasie byłem przyzwyczajony do hostingu „typu Cpanel”, w którym był wygodny, poręczny GUI do konfigurowania zadań crona w tym właśnie celu.

Po wielu badaniach znalazłem fajne rozwiązanie — Celery, potężną asynchroniczną kolejkę zadań używaną do uruchamiania zadań w tle. Ale to doprowadziło do dodatkowych problemów, ponieważ nie mogłem znaleźć łatwego zestawu instrukcji do integracji Celery z projektem Django.

Oczywiście w końcu udało mi się to rozgryźć — o czym będzie mowa w tym artykule:Jak zintegrować Celery z projektem Django i tworzyć zadania okresowe.

Bezpłatny bonus: Kliknij tutaj, aby uzyskać dostęp do bezpłatnego przewodnika po zasobach edukacyjnych Django (PDF), który zawiera wskazówki i triki, a także typowe pułapki, których należy unikać podczas tworzenia aplikacji internetowych Python + Django.

Ten projekt wykorzystuje Python 3.4, Django 1.8.2, Celery 3.1.18 i Redis 3.0.2.


Przegląd

Dla Twojej wygody, ponieważ jest to tak duży post, wróć do tej tabeli, aby uzyskać krótkie informacje na temat każdego kroku i pobrać powiązany kod.

Krok Przegląd Tag Git
Boiler Pobierz schemat v1
Konfiguracja Zintegruj Celery z Django v2
Zadania dotyczące selera Dodaj podstawowe zadanie z selerem v3
Zadania okresowe Dodaj zadanie okresowe v4
Działa lokalnie Uruchom naszą aplikację lokalnie v5
Uruchamianie zdalne Uruchom naszą aplikację zdalnie v6


Co to jest seler?

„Celery to asynchroniczna kolejka zadań/kolejka zadań oparta na przekazywaniu wiadomości rozproszonych. Koncentruje się na działaniu w czasie rzeczywistym, ale obsługuje również planowanie”. W tym poście skupimy się na funkcji planowania okresowego uruchamiania zadania/zadania.

Dlaczego jest to przydatne?

  • Pomyśl o wszystkich przypadkach, w których musiałeś wykonać określone zadanie w przyszłości. Być może potrzebujesz dostępu do interfejsu API co godzinę. A może pod koniec dnia musiałeś wysłać partię e-maili. Duży czy mały, Seler ułatwia planowanie takich okresowych zadań.
  • Nigdy nie chcesz, aby użytkownicy końcowi musieli niepotrzebnie czekać na załadowanie stron lub zakończenie działań. Jeśli długi proces jest częścią przepływu pracy Twojej aplikacji, możesz użyć programu Celery do wykonania tego procesu w tle, gdy zasoby staną się dostępne, dzięki czemu aplikacja będzie mogła nadal odpowiadać na żądania klientów. Utrzymuje to zadanie poza kontekstem aplikacji.


Konfiguracja

Przed zanurzeniem się w Seler, pobierz projekt startowy z repozytorium Github. Upewnij się, że aktywujesz virtualenv, zainstaluj wymagania i uruchom migracje. Następnie uruchom serwer i przejdź do http://localhost:8000/ w swojej przeglądarce. Powinieneś zobaczyć znajomy tekst „Gratulacje na twojej pierwszej stronie opartej na Django”. Po zakończeniu zabij serwer.

Następnie zainstalujmy Selera za pomocą pip:

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

Teraz możemy zintegrować Celery z naszym projektem Django w zaledwie trzech prostych krokach.


Krok 1:dodaj celery.py

W katalogu „picha” utwórz nowy plik o nazwie 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))

Zwróć uwagę na komentarze w kodzie.



Krok 2:zaimportuj nową aplikację Celery

Aby upewnić się, że aplikacja Celery zostanie załadowana podczas uruchamiania Django, dodaj następujący kod do __init__.py plik znajdujący się obok pliku settings.py plik:

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

Po wykonaniu tej czynności układ projektu powinien teraz wyglądać tak:

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


Krok 3:Zainstaluj Redis jako „broker” selera

Celery używa „brokerów” do przekazywania wiadomości między projektem Django a pracownikami Celery. W tym samouczku użyjemy Redis jako brokera wiadomości.

Najpierw zainstaluj Redis z oficjalnej strony pobierania lub przez brew (brew install redis ), a następnie przejdź do terminala, w nowym oknie terminala uruchom serwer:

$ redis-server

Możesz sprawdzić, czy Redis działa poprawnie, wpisując to w terminalu:

$ redis-cli ping

Redis powinien odpowiedzieć PONG - spróbuj!

Po uruchomieniu Redis dodaj następujący kod do pliku settings.py:

# 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'

Musisz również dodać Redis jako zależność w projekcie Django:

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

Otóż ​​to! Powinieneś teraz móc używać Celery z Django. Aby uzyskać więcej informacji na temat konfigurowania programu Celery z Django, zapoznaj się z oficjalną dokumentacją programu Celery.

Zanim przejdziemy dalej, przeprowadźmy kilka kontroli zdrowia psychicznego, aby upewnić się, że wszystko jest w porządku…

Sprawdź, czy pracownik Seler jest gotowy do przyjmowania zadań:

$ 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

Zakończ proces za pomocą CTRL-C. Teraz sprawdź, czy harmonogram zadań Celery jest gotowy do działania:

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

Bum!

Ponownie zakończ proces po zakończeniu.




Zadania dotyczące selera

Celery wykorzystuje zadania, które można traktować jako zwykłe funkcje Pythona, które są wywoływane przez Celery.

Na przykład zamieńmy tę podstawową funkcję w zadanie Seler:

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

Najpierw dodaj dekoratora:

from celery.decorators import task

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

Następnie możesz uruchomić to zadanie asynchronicznie z Celery w ten sposób:

add.delay(7, 8)

Proste, prawda?

Tak więc tego typu zadania są idealne, gdy chcesz załadować stronę internetową bez zmuszania użytkownika do czekania na zakończenie jakiegoś procesu w tle.

Spójrzmy na przykład…

Wracając do projektu Django, pobierz wersję trzecią, która zawiera aplikację, która przyjmuje opinie od użytkowników, trafnie nazywaną feedback :

├── 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

Zainstaluj nowe wymagania, uruchom aplikację i przejdź do http://localhost:8000/feedback/. Powinieneś zobaczyć:

Połączmy zadanie Seler.


Dodaj zadanie

Zasadniczo, gdy użytkownik prześle formularz opinii, chcemy natychmiast pozwolić mu kontynuować swoją wesołą drogę, podczas gdy przetwarzamy opinię, wysyłamy e-mail itp., wszystko w tle.

Aby to osiągnąć, najpierw dodaj plik o nazwie tasks.py do katalogu „opinie”:

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)

Następnie zaktualizuj forms.py tak:

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'])

Zasadniczo send_feedback_email_task.delay(email, message) funkcja przetwarza i wysyła wiadomość e-mail z informacją zwrotną w tle, gdy użytkownik kontynuuje korzystanie z witryny.

UWAGA :success_url w views.py jest ustawione na przekierowanie użytkownika do / , który jeszcze nie istnieje. Skonfigurujemy ten punkt końcowy w następnej sekcji.




Zadania okresowe

Często trzeba zaplanować uruchamianie zadania o określonej godzinie co jakiś czas — np. web scraper może być uruchamiany codziennie. Takie zadania, zwane zadaniami okresowymi, są łatwe do skonfigurowania z Selerem.

Seler używa „beatów selera” do planowania zadań okresowych. Celery beat wykonuje zadania w regularnych odstępach czasu, które są następnie wykonywane przez pracowników selera.

Na przykład następujące zadanie jest zaplanowane do uruchomienia co piętnaście minut:

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

Spójrzmy na solidniejszy przykład, dodając tę ​​funkcjonalność do projektu Django…

Wracając do projektu Django, pobierz wersję czwartą, która zawiera kolejną nową aplikację o nazwie photos , który używa interfejsu API Flickr do pobierania nowych zdjęć do wyświetlenia w witrynie:

├── 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

Zainstaluj nowe wymagania, uruchom migracje, a następnie uruchom serwer, aby upewnić się, że wszystko jest w porządku. Spróbuj ponownie przetestować formularz opinii. Tym razem powinno dobrze przekierować.

Co dalej?

Cóż, ponieważ musielibyśmy okresowo wywoływać Flickr API, aby dodać więcej zdjęć do naszej witryny, możemy dodać zadanie Celery.


Dodaj zadanie

Dodaj tasks.py do photos aplikacja:

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")

Tutaj uruchamiamy save_latest_flickr_image() działać co piętnaście minut, umieszczając wywołanie funkcji w task . @periodic_task dekorator wyodrębnia kod do uruchomienia zadania Celery, pozostawiając tasks.py plik czysty i łatwy do odczytania!




Działa lokalnie

Gotowy do uruchomienia tego?

Po uruchomieniu aplikacji Django i Redis otwórz dwa nowe okna/karty terminala. W każdym nowym oknie przejdź do katalogu projektu, aktywuj virtualenv, a następnie uruchom następujące polecenia (po jednym w każdym oknie):

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

Kiedy odwiedzasz witrynę pod adresem http://127.0.0.1:8000/, powinieneś zobaczyć teraz jeden obraz. Nasza aplikacja otrzymuje jedno zdjęcie z serwisu Flickr co 15 minut:

Spójrz na photos/tasks.py aby zobaczyć kod. Kliknięcie przycisku „Opinia” umożliwia… przesłanie opinii:

Działa to poprzez zadanie selerowe. Spójrz na feedback/tasks.py po więcej.

To wszystko, masz już uruchomiony projekt Picha!

Jest to dobre do testowania podczas lokalnego opracowywania projektu Django, ale nie działa tak dobrze, gdy trzeba wdrożyć w środowisku produkcyjnym - jak być może na DigitalOcean. W tym celu zaleca się uruchomienie pracownika i programu planującego Celery w tle jako demona z Supervisorem.



Bieganie zdalne

Instalacja jest prosta. Pobierz wersję piątą z repozytorium (jeśli jeszcze jej nie masz). Następnie SSH do zdalnego serwera i uruchom:

$ sudo apt-get install supervisor

Następnie musimy poinformować nadzorcę o naszych pracownikach Celery, dodając pliki konfiguracyjne do katalogu „/etc/supervisor/conf.d/” na zdalnym serwerze. W naszym przypadku potrzebujemy dwóch takich plików konfiguracyjnych - jednego dla pracownika Celery i jednego dla harmonogramu Celery.

Lokalnie utwórz folder o nazwie „nadzorca” w katalogu głównym projektu. Następnie dodaj następujące pliki…

Pracownik selera: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

Harmonogram selera: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

Upewnij się, że zaktualizowałeś ścieżki w tych plikach, aby pasowały do ​​systemu plików zdalnego serwera.

Zasadniczo te pliki konfiguracyjne nadzorcy informują nadzorcę, jak uruchamiać i zarządzać naszymi „programami” (jak nazywa je nadzorca).

W powyższych przykładach stworzyliśmy dwa programy nadzorowane o nazwach „pichacelery” i „pichacelerybeat”.

Teraz po prostu skopiuj te pliki na zdalny serwer w katalogu „/etc/supervisor/conf.d/”.

Musimy również utworzyć pliki dziennika wymienione w powyższych skryptach na zdalnym serwerze:

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

Na koniec uruchom następujące polecenia, aby poinformować nadzorcę o programach — np. pichacelery i pichacelerybeat :

$ sudo supervisorctl reread
$ sudo supervisorctl update

Uruchom następujące polecenia, aby zatrzymać, uruchomić i/lub sprawdzić stan pichacelery program:

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

Możesz przeczytać więcej o Supervisorze z oficjalnej dokumentacji.



Końcowe wskazówki

  1. Nie przekazuj obiektów modelu Django do zadań Celery. Aby uniknąć przypadków, w których obiekt modelu już się zmienił przed przekazaniem go do zadania Selera, przekaż klucz podstawowy obiektu do Selera. Wtedy oczywiście musiałbyś użyć klucza podstawowego, aby pobrać obiekt z bazy danych przed rozpoczęciem pracy nad nim.
  2. Domyślny harmonogram Celery tworzy kilka plików, aby przechowywać swój harmonogram lokalnie. Te pliki to „celerybeat-schedule.db” i „celerybeat.pid”. Jeśli używasz systemu kontroli wersji, takiego jak Git (co powinieneś!), dobrym pomysłem jest zignorowanie tych plików i nie dodawanie ich do repozytorium, ponieważ służą one do lokalnego uruchamiania procesów.


Dalsze kroki

Cóż, to tyle, jeśli chodzi o podstawowe wprowadzenie do integracji Celery z projektem Django.

Chcesz więcej?

  1. Zagłęb się w oficjalny podręcznik użytkownika Selera, aby dowiedzieć się więcej.
  2. Utwórz plik Fab, aby skonfigurować administratora i pliki konfiguracyjne. Pamiętaj, aby dodać polecenia do reread i update Nadzorca.
  3. Rozwiń projekt z repozytorium i otwórz Pull Request, aby dodać nowe zadanie Seler.

Bezpłatny bonus: Kliknij tutaj, aby uzyskać dostęp do bezpłatnego przewodnika po zasobach edukacyjnych Django (PDF), który zawiera wskazówki i triki, a także typowe pułapki, których należy unikać podczas tworzenia aplikacji internetowych Python + Django.

Miłego kodowania!



  1. Database
  2.   
  3. Mysql
  4.   
  5. Oracle
  6.   
  7. Sqlserver
  8.   
  9. PostgreSQL
  10.   
  11. Access
  12.   
  13. SQLite
  14.   
  15. MariaDB
  1. Model bazy danych dla platformy MOOC

  2. Ciekawe rzeczy o wyzwalaczach ZAMIAST

  3. Niezamierzone skutki uboczne – sesje snu trzymające kłódki

  4. Dzielenie ciągów:teraz z mniejszą ilością T-SQL

  5. Jak zmienić nazwę tabeli w SQL?