Asynchroniczne zadania - Celery w projektach Django

Celery to asynchroniczna kolejka zadań zlecanych przez np. aplikacje webowe. Zadania mogą być wykonywane asynchronicznie - aplikacja zlecająca nie czeka na wynik, lub synchronicznie - z oczekiwaniem na wynik. Zadania wykonywane mogą być współbieżnie na jednym lub wielu serwerach. Cały ten system ma już za sobą liczne udane wdrożenia, a jego główne zadanie to odciążanie aplikacji webowych od wykonywania długotrwałych operacji związanych z jakąś akcją (np. generowanie miniatur, wysyłanie żądań do zewnętrznych API itd.).

Dostępne są także integracje z popularnymi frameworkami, np. django-celery, celery-pylons, czy flask-celery.

Instalacja Celery i zależności

Celery można zainstalować poprzez pip lub easy_install:
pip install Celery
easy_install Celery

pip install django-celery
easy_install django-celery
Potrzebujemy też brokera RabittMQ rozdzielającego zadania pomiędzy wykonującymi je "workerami". W przypadku dystrybucji Linuksa odpowiedni pakiet powinien być w repozytorium (np. rabbitmq-server dla Ubuntu/Debiana i pochodnych). W przypadku wspomnianego Ubuntu/Debiana dostajemy gotowy do użytku serwer RabbitMQ (pakiet się instaluje, po czym startuje serwer). W przypadku innych dystrybucji konieczna może okazać się ręczna konfiguracja/uruchomienie serwera.

Integracja z Django

Django-celery zapewnia nam integrację z frameworkiem Django. Po instalacji pakietu musimy skonfigurować projekt Django. Do INSTALLED_APPS dodajemy "djcelery", oraz:
import djcelery
djcelery.setup_loader()
Dodajemy też konfigurację RabitMQ. Podstawowa wygląda tak (dodajemy do settings.py):
BROKER_HOST = "127.0.0.1"
BROKER_PORT = 5672
BROKER_VHOST = "/"
BROKER_USER = "guest"
BROKER_PASSWORD = "guest"
Następnie wykonujemy python manage.py syncdb by stworzyć wszystkie potrzebne tabele i gotowe. Jeżeli używamy mod_wsgi to do konfiguracji dodajemy:
import os
os.environ["CELERY_LOADER"] = "django"
By uruchomić Celery dla Django w konsoli (dla środowiska developerskiego) wykonujemy:
manage.py celeryd -l info
W drugim oknie odpalamy projekt Django i możemy przystąpić do testowania przygotowanych zadań... ale o tym za chwilę. Jeżeli pojawi się błąd typu:
Consumer: Connection Error: [Errno 111] Connection refused. Trying again in 2 seconds...
To oznacza iż albo RabitMQ nie jest zainstalowany, albo nie działa (albo konfiguracja jest niewłaściwa).

Tworzenie zadań dla Celery

Zadania dla Celery w aplikacji Django powinny znajdować się w pliku tasks.py. Szkielet zadania (Task) wygląda następująco:
from celery.decorators import task

@task()
def add(x, y):
    return x + y
Mamy prostą funkcję oznaczoną dekoratorem "task". By zlecić wykonanie tego zadania wystarczy wywołać je tak:
from moja_aplikacja.tasks import NazwaZadania
NazwaZadania.delay(some_arg="foo")

W konsoli z działającym Celery powinniśmy widzieć napływające zadania do wykonania. Notka: jeżeli dodałeś lub zmodyfikowałeś jakieś zadanie to zrestartuj Celery (manage.py celeryd) by zmiany zostały zauważone.

Powyższy kod szkieletowy już działa, lecz Celery oferuje tonę opcji i możliwości. Dokładny opis znajdziemy w dokumentacji, a różne gotowe rozwiązania na blogach, czy w prezentacjach. Dla przykładu warto zadbać o odpowiednie logi z wykonywanego zadania (szczególnie gdy jest złożone). Można zrobić to tak:
@task
def FooBarTask():
        """
        Just a showcase task for Celery :)
        """
        logger = FooBarTask.get_logger()
        logger.info('Starting FooBarTask')
        
        loop = 1
        while loop < 10000000:
                loop += 1
        
        logger.info('Finished FooBarTask')
W konsoli Celery powinniśmy zauważyć wiadomości z logów:
[2011-12-30 10:19:38,425: INFO/MainProcess] Got task from broker: myapp.tasks.FooBarTask[0709d0cf-01cb-47e1-b7a3-ba1164bd4e84]
[2011-12-30 10:19:38,444: INFO/PoolWorker-3] myapp.tasks.FooBarTask[0709d0cf-01cb-47e1-b7a3-ba1164bd4e84]: Starting FooBarTask
[2011-12-30 10:19:38,900: INFO/PoolWorker-3] myapp.tasks.FooBarTask[0709d0cf-01cb-47e1-b7a3-ba1164bd4e84]: Finished FooBarTask
[2011-12-30 10:19:38,969: INFO/MainProcess] Task myapp.tasks.FooBarTask[0709d0cf-01cb-47e1-b7a3-ba1164bd4e84] succeeded in 0.525212049484s: None

I to by było na tyle z podstaw Celery :)

RkBlog

Django, 31 December 2011

Comment article
Comment article RkBlog main page Search RSS Contact