Ось твій урок у стилі CS50. Вмикаймо світло, виходимо на сцену! 🎙️
🎓 CS50: Celery Beat — Як підкорити час у Python
Привіт, світе! 👋 Я радий бачити вас тут.
Сьогодні ми не просто пишемо код. Сьогодні ми вчимося керувати четвертим виміром — часом.
Ви вже знаєте, як виконувати задачі асинхронно за допомогою Celery (пам'ятаєте нашу аналогію з кухарями на кухні?). Але що, як нам потрібно, щоб кухар починав готувати суп рівно о 6:00 ранку? Або щоб сміття виносили щовівторка?
Ми не можемо сидіти й чекати, щоб натиснути кнопку "Start". Нам потрібен ритм. Нам потрібен Celery Beat.
1. 🔥 Вступ: Проблема «Вічного очікування»
Уявіть, що ви керуєте банком. Вам потрібно нарахувати відсотки по депозитах усім клієнтам. Це має статися рівно о 00:01 першого числа кожного місяця.
Питання до вас: Як би ви це зробили, маючи лише звичайний Python-скрипт?
Можливо, ви напишете щось таке:
while True:
if datetime.now() == first_of_month:
pay_money()
time.sleep(1) # Спимо секунду і перевіряємо знову
Виглядає логічно? Можливо. Але це жахіття. 1. Що, якщо скрипт "впаде" за 5 хвилин до півночі? 2. Що, якщо сервер перезавантажиться? 3. Що, якщо перевірка займе більше часу і ми пропустимо точну секунду?
Це якби ви поставили будильник, але замість того, щоб спати, ви всю ніч дивилися на годинник, чекаючи, поки стрілки зійдуться. Це неефективно. Це виснажливо.
Ось навіщо нам Celery Beat. Це наш надійний "будильник", який живе окремо від коду і просто штурхає систему: "Гей, час настав! Зроби це!".
2. 🧠 Теоретична база: Диригент і Оркестр
Давайте заглянемо "під капот", але без нудних схем.
У світі Celery є два головних персонажі: 1. Worker (Робітник): Це м'язи. Він отримує задачу і виконує її (відправляє email, обробляє зображення). 2. Beat (Ритм/Диспетчер): Це мозок із годинником.
Як це працює? (Механіка)
Уявіть оркестр. * Worker — це скрипаль. Він вміє грати, але сидить тихо і чекає. * Beat — це диригент. Він не грає на скрипці! Він дивиться в партитуру (ваш розклад), бачить ноту і в потрібний момент вказує паличкою на скрипаля.
Процес крок за кроком: 1. Beat перевіряє розклад (файл налаштувань). 2. Бачить: "Ага, о 12:00 треба відправити звіт". 3. Beat створює задачу (повідомлення) і кидає її в Брокер (Redis/RabbitMQ). 4. Beat засинає до наступної задачі. 5. Worker бачить нову задачу в Брокері, хапає її і виконує.
❗️ Запам'ятайте головне: Celery Beat НЕ ВИКОНУЄ задачі. Він їх лише ПЛАНУЄ (ставить у чергу). Якщо у вас запущений Beat, але немає Worker'ів — задачі просто будуть накопичуватися в черзі, і ніхто їх не зробить.
3. 🧪 Приклади: Від «Привіт» до Crontab
Давайте подивимось на код.
Рівень 1: Простий інтервал
Уявіть, що ми хочемо перевіряти статус сервера кожні 30 секунд.
У файлі celery_app.py (або settings.py в Django):
from celery import Celery
app = Celery('my_app', broker='redis://localhost:6379/0')
# Налаштування розкладу
app.conf.beat_schedule = {
'say-hello-every-30s': {
'task': 'tasks.print_hello', # Шлях до функції
'schedule': 30.0, # Кожні 30 секунд
'args': ('Світе',), # Аргументи функції
},
}
А ось сама задача в tasks.py:
@app.task
def print_hello(name):
print(f"Привіт, {name}! Я працюю.")
Що тут відбувається? Beat кожні 30 секунд створюватиме задачу print_hello. Якщо у вас запущений Worker, він її підхопить і виведе текст у консоль.
Рівень 2: Магія Crontab (Для дорослих задач)
Інтервали — це добре. Але реальний світ живе за календарем. Як сказати: "Запускай це щопонеділка о 8:00 ранку"?
Для цього ми використовуємо Crontab. Це стандарт Unix для опису часу.
from celery.schedules import crontab
app.conf.beat_schedule = {
'send-weekly-digest': {
'task': 'tasks.send_email',
# Слідкуйте за руками:
# minute=0, hour=8 (8:00), day_of_week=1 (Понеділок)
'schedule': crontab(minute=0, hour=8, day_of_week=1),
},
}
Питання на засипку: Що буде, якщо я напишу crontab(minute='*', hour=8)?
Відповідь: Задача запускатиметься щохвилини протягом восьмої години (8:00, 8:01... 8:59). Будьте обережні з зірочками (*)!
4. 🛠 Практична частина
Час забруднити руки. Відкривайте термінал!
Завдання 1: "Живий пульс"
Налаштуйте Celery Beat так, щоб він кожні 10 секунд писав у лог фразу "System is alive".
1. Запустіть Worker в одному терміналі: celery -A my_project worker --loglevel=info
2. Запустіть Beat в іншому: celery -A my_project beat --loglevel=info
3. Спостерігайте, як вони спілкуються.
Завдання 2: "Ранкова кава"
Змініть розклад так, щоб задача виконувалася щодня у той час, який зараз у вас на годиннику + 2 хвилини.
Підказка: Використовуйте crontab(hour=..., minute=...).
Завдання 3: "Понеділок — день важкий"
Напишіть конфігурацію crontab, яка запускатиме задачу тільки по буднях о 9:00 та о 18:00.
(Подумайте, як передати список значень у crontab).
Завдання 4: Міні-кейс "Чистка сміття"
У вас є функція clean_old_logs(). Вона видаляє файли старше 30 днів.
Реалізуйте розклад: запускати цю чистку кожну ніч о 03:30, але тільки якщо це неділя.
Завдання 5: "А що, якщо..."
Що станеться, якщо ваша задача виконується 5 хвилин, а в розкладі вона стоїть кожну 1 хвилину?
Експеримент: Зробіть time.sleep(10) у задачі, поставте розклад на 5 секунд. Подивіться на чергу. Чи це добре?
5. 💡 Мислення як у розробника
Як відрізнити новачка від профі, коли мова йде про Celery Beat?
❌ Типова помилка новачка: "Один процес для всього"
Новачки часто думають, що команда celery worker автоматично включає і розклад. Ні!
Beat — це окремий процес. У продакшені (на сервері) вам потрібно запустити два демони (services): один для Worker, один для Beat. Якщо Beat впаде, задачі перестануть створюватися, хоча Worker буде готовий працювати.
❌ Пастка з часовими поясами (Timezones)
Це класика. Ви ставите задачу на 8:00 ранку. Вона спрацьовує, але... о 5:00 або об 11:00.
Чому? Сервери часто живуть за часом UTC. А ви — за Києвом.
Порада профі: Завжди явно вказуйте CELERY_TIMEZONE = 'Europe/Kyiv' (або UTC) і розумійте, в якому часі живе ваш сервер. Краще зберігати все в UTC, а конвертувати тільки для відображення юзеру.
❌ Дублювання Beat
Ніколи не запускайте два процеси Beat одночасно для одного проекту. Вони не знають про існування одне одного і будуть створювати дублі задач. Ви отримаєте два листи замість одного. Beat має бути один (Singleton).
6. 🧩 Підсумок
Отже, що ми сьогодні зробили?
1. Ми зрозуміли, що while True: sleep() — це погана ідея для розкладу.
2. Ми дізналися, що Beat — це диригент, а Worker — музикант.
3. Ми навчилися керувати часом за допомогою interval та crontab.
Тепер ваш код може жити своїм життям, навіть коли ви спите. Він може надсилати вітання, чистити базу даних, парсити курси валют — і все це автоматично.
Що далі? Тепер, коли ви вмієте запускати задачі, постає питання: А як дізнатися, чи успішно вони виконалися? На наступному занятті ми поговоримо про Моніторинг і інструмент під назвою Flower. Бо довіряй, але перевіряй! 🌸
А поки що... це був CS50. Щасти вам з кодом! 💻