Celery дает возможность положить задачу в отдельную очередь и выполнять их в порядке очереди отдельным процессом.
Это позволяет не тормозить основной процесс всякими долгоживущими задачами и отлаживать их.
Для работы Celery нам понадобися база данных Redis.
Установка Redis.
Создадим новый контейнер в файле docker-compose.yaml.
redis-server:
image: "redis:alpine"
Запуск контейнера.
docker-compose run redis-server
Включим его запуск в tmux
split-window -h \; \
...
select-pane -t 2 \;
send-keys 'docker-compose run redis-server' C-m \; \
Установка Celery.
pip install celery redis
Далее нам необходимо создать приложение celery в новом файле celery_app.py рядом с settings.py.
from celery import Celery
from django.conf import settings
from celery.schedules import crontab
import os
os.environ.setdefault('DJANGO_SETTINGS_MODULE', 'prj.settings')
app = Celery('prj')
app.config_from_object('django.conf:settings', namespace='CELERY')
app.autodiscover_tasks(lambda: settings.INSTALLED_APPS)
Добавить импорт в init.py
from .celery_app import app as celery_app
В settings.py установить Redis в качестве брокера сообщений.
REDIS_HOST = os.getenv('SQL_HOST', 'localhost')
REDIS_PORT = os.getenv('SQL_PORT', '6379')
CELERY_BROKER_URL = 'redis://' + REDIS_HOST + ':' + str(REDIS_PORT)
Создадим новый контейнер под очередь задач celery.
celery-tasks:
build: .
restart: always
working_dir: /app
command: celery -A prj worker -l info
volumes:
- ./prj:/app
depends_on:
- redis-server
env_file:
- ./.env.dev
container_name: celery-tasks
Для того, чтобы запускать задачи по заданному расписанию нам понадобится запускать отдельный процесс, который будет ложить в очередь задачи по заданному интервалу времени.
Этот процесс называется cellery-beat.
Запускается он командой.
celery -A prj beat
Создадим задачу, выводящую текущее время на экран в файле celery.py
from datetime import datetime
@app.task(bind=True)
def timer_task(self):
now = datetime.now()
current_time = now.strftime("%H:%M:%S")
print(f'Time is: {current_time}')
bind=True - позволяет обращатся к экземпляру задачи внутри ее самой например так self.retry(countdown=5)
Далее создадим планировщик и запустим задачу с интервалом в 1 мин.
app.conf.beat_schedule = {
'timer-task': {
'task': 'prj.celery.timer_task',
'schedule': crontab(minute='*/1'),
}
}
ссылка на документацию по интервалам
Создадим отдельный докер контейнер под celery-beat.
celery-beat:
build: .
restart: always
working_dir: /app
command: celery -A prj beat
volumes:
- ./prj:/app
depends_on:
- redis-server
env_file:
- ./.env.dev
container_name: celery-beat
Файл для запуска всех процессов в отдельных окнах tmux.
#!/bin/bash
command -v tmux >/dev/null 2>&1 || { echo >&2 "I require tmux but it's not installed. Aborting."; exit 1; }
tmux new-session \; \
split-window -v \; \
split-window -h \; \
select-pane -t 0 \; \
split-window -h \; \
split-window -h \; \
send-keys 'docker-compose run db' C-m \; \
select-pane -t 4 \; \
send-keys 'docker-compose run django' C-m \; \
select-pane -t 3 \; \
send-keys 'docker-compose run redis-server' C-m \; \
select-pane -t 0 \; \
send-keys 'docker-compose run celery-tasks' C-m \; \
select-pane -t 1 \; \
send-keys 'docker-compose run celery-beat' C-m \; \
Результат.