Scaling a Django App with Celery
Celery is a library for asynchronous task and worker queues and scheduling based on distributed message passing infrastructure like RabbitMQ (or Redis) written in Python. It facilitates the distribution of work of intensive parts of the application among different processes to horizontally scale an application across all systems. Celery can easily be integrated to a Django application to make it more responsive, distributed and scalable.
Getting Started with Celery
First, we install Celery with the PIP Package Manager.
Disclaimer: Since Celery 3.1 we don’t need to add the package celery-django to the Django application anymore. Everything works fine with solely the Celery package.
Now we add the file celery.py for the global configuration of Celery to the project folder my_project; the structure of project directory now looks like this:
- setup.py
- requirements.txt
- my_project
- manage.py
- my_project
- __init__.py
- celery.py
- settings.py
- urls.py
- views.py
- wsgi.py
To tell Celery it should start the workers with the settings of my_project we modifiy the celery.py file to look like this:
Configure a Message Passing System
Celery relies on an existing messages passing system; in this example we use RabbitMQ as a reliable and scalable infrastructure.
This installs and starts RabbitMQ on it default port 5672 and a user guest with password guest preconfigured for local use. We can check the RabbitMQ documentation and adapt the settings according to our needs.
Now, we need to configure a BROKER_URL in the settings of the Django project, so we add following configuration to the settings.py file:
Adding Tasks for Workers
We want Celery to distribute intensive tasks from the my_app application of the my_project project among all systems; therefore we need to tell Celery which tasks should be executed by asynchronous workers. We add a tasks.py file to the application specific directory my_app.
- setup.py
- requirements.txt
- my_project
- manage.py
- my_app
- __init__.py
- tasks.py
- admin.py
- models.py
- urls.py
- views.py
- tests.py
- my_project
- __init__.py
- celery.py
- settings.py
- urls.py
- views.py
- wsgi.py
We create a function run_task that starts the work intensive task and a success and error callback - that are again executed asynchronously - inside the tasks.py.
Now, we can call these asynchronous tasks anywhere in the Django project via
This syntax uses the signature method s to add the callbacks and execute the task asynchronously. If you don’t want to apply callbacks, check out the Celery documentation for an easier syntax like run_task.apply_async(args, kwargs, **options)!
Run the Workers
To run a worker daemon that is waiting for tasks to execute, simply start celery in the root folder of the project.