Slow HTTP Response Times? Celery to the Rescue!
Learn how to offload computationally intensive tasks in Python

In an increasingly data-driven world, it is important for software engineers to build applications that are responsive and fast. As our software needs become more complex, our computing requirements also escalate. At the same time, users expect responsive and snappy performance from applications they interact with on a daily basis.
Long-running background tasks are a necessary part of a majority of software applications today. How do we engineer software that gracefully handles such tasks without comprising on user experience? Imagine if a user had to keep a tab open to finish an upload or send an email. How dreadful would that be from a user experience perspective? What we need is a way to process tasks in the background and in an asynchronous manner.
Celery and RabbitMQ
We can use the Celery framework for Python to handle background tasks.
Celery is an asynchronous task queue framework written in Python. Essentially, you can think of it as a queue data structure with the FIFO (first in, first out) principle.
So the first task you put into the queue gets processes first. This makes it possible to schedule tasks. It also ensures tasks are processed in the order that they are received by the application.

Celery uses the paradigm of distributed message passing which means it includes a message queue as well. In this post, we will use RabbitMQ as the message queue.
Now you might be wondering, what is the difference between a task queue and a message queue.
You can think of a task queue as a way to distribute computation across machines or processes. A worker is an abstraction provided by Celery that represents a parent process. A worker spawns some child processes to handle individual tasks as clients use our application. The processes spawned would scan the task queue looking for tasks to perform.
However, to send and receive these tasks we need something like a message queue also known as a message broker. This is where RabbitMQ comes in. RabbitMQ would be responsible for sending tasks from the task queue to the worker that needs them. Note it is possible to use a solution called Redis but we will stick to RabbitMQ for this post.
Let’s get on with coding now!
Setup
Note: I was not able to get things running on my Windows machine. This post assumes a Linux or macOS environment.
We will build a simple Flask application that sends emails to motivate the use of Celery.
Begin by installing the following Python packages:
pip3 install yagmail
Next, install the Celery package:
pip3 install celery
Unfortunately, due to security reasons, it is not possible to simply send an email programmatically. If you use Gmail you can follow the guide here under “Create & use App Passwords” to set up a one-time password for your Gmail account. Remember to note down the password.
Next, create a file named default_settings.py and enter the following constants where you replace with your email and password:
MAIL_USERNAME = <Username>
MAIL_PASSWORD = <One-time password>
Next, install RabbitMQ from here (Linux) and here (macOS).
You can ensure RabbitMQ is running using the following command:
brew services list
The terminal should something like the following:

Great! Let’s code!
Initialize Celery with RabbitMQ
We begin by initializing a Celery app: