Используем Celery с Django.

Celery дает возможность положить задачу в отдельную очередь и выполнять их в порядке очереди отдельным процессом.

Это позволяет не тормозить основной процесс всякими долгоживущими задачами и отлаживать их.

start page

Для работы 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 \; \

Результат.

start page