Автор: Михайло Ашкалунін, Developer/Python support
Матеріали статті підготовлені в рамках внутрішньої програми менторства і навчання фахівців команди Evergreen.
Сьогодні поговоримо про Celery — популярний інструмент для створення розподілених черг задач. Такі черги дозволяють розвантажити роботу в інший процес і виконувати частину задач у фоновому режимі (якщо вони не залежать від подальших дій користувачів), поки застосунок виконує інші задачі. Це прискорює час відгуку сайту чи програми і покращує user experience. Також поговоримо про Flower — з його допомогою ми відслідковуємо воркери Celery та прогрес по тасках.
Коротко про те, чим є Celery:
Celery зарекомендував себе як чудовий інструмент для:
Розглянемо коротко, з чого складається архітектура Celery.
В Celery задачі створюють так звані “продюсери” (producers). Ними може виступати Celery beat (cron), який генеруватиме таски щохвилини, щогодини — як ми запрограмуємо, або ж задачі будуть генеруватись з програми (не обов’язково Django) за подією: коли користувач щось натиснув на фронті, на бекенді згенерувалась задача.
Таски потрапляють у чергу в брокер (Redis або RabbitMQ). Ми використовуємо Redis, оскільки його легше налаштувати, і він краще справляється з простими задачами. В брокері можна організувати декілька черг і присвоїти кожній пріоритет залежно від важливості.
Consumers — власне, воркери (workers) — це виконавці задач, які забирають і виконують таски з черг у брокері. Для кожного воркера черга налаштована індивідуально. Воркери можуть повертати або не повертати результати. Якщо результати виконання задачі нам не потрібні, можемо їх ігнорувати. Якщо потрібні, вони зберігатимуться в бекенді Celery у вигляді даних типу success і hash та інформації про час (тривалість) виконання задачі. Як бекенд також може використовуватись Redis.
Ділимость нашими спостереженнями і висновками, зробленими в процесі роботи з чергами задач в Celery.
База даних не зовсім для цього призначена і має свої обмеження: вона не настільки швидка і розміщується, на відміну від Redis, не в оперативній пам’яті, а на жорсткому диску. Навіть якщо це SSD, все рівно потрібно набагато більше часу, щоб її використовувати.
Щоб другорядні таски не забивали всі воркери, нам потрібно залишати окремий воркер під високопріоритетну чергу. Так критичні таски, які потрібно виконати негайно, будуть запускатись миттєво, а не чекати завершення низькопріоритетних задач, якими забита черга.
По-перше, все це зберігається в брокері в той час, поки задача перебуває в черзі (в Redis, який “висить” в оперативці) і забиває пам’ять. По-друге, якщо ми отримали ORM-об’єкт, до того моменту, коли задача дійде до виконання, дані в цьому об’єкті вже можуть бути неактуальними. Тобто потрібно зробити повторний запит в БД, отримати оновлені дані та їх обробляти.
Рекомендуємо вказувати ліміт для кожної задачі, в тому числі низькопріоритетних. Це потрібно, щоб запобігти ситуації, коли неоднозначні таски зависають на виконанні і гальмують роботу всієї системи.
Це корисно для модульності, скорочує кількість коду (багатомодульність), і задачі не висять тривалий час на виконанні.
Якщо ми обробляємо і зберігаємо результати виконання задач, з них має бути зрозуміло, що відбувається з тасками. Звичайно, це не має бути великий об’єкт чи JSON, але і не проста відповідь true or false.
В момент запуску Redis, де зберігаються результати виконання задач, ці дані можуть бути втрачені. Якщо ми ведемо логи, все зводиться у певний файл, і в разі необхідності ми можемо прослідкувати, що відбувалось з тасками не тільки за результатами, але і в процесі виконання.
Це must have. Для доступу до моніторингу вистачить браузера — не потрібно заходити в консоль, на сервер, при цьому керування доступом у Flower значно простіше і візуально зрозуміліше.
При роботі з Celery рекомендуємо використовувати Flower — невеликий веб-застосунок, головними можливостями якого є:
В налаштуваннях ми почали використовувати autoscale: якщо не прописувати автоскейлінг або concurrency, то кількість субпроцесів буде дорівнювати кількості ядер. І це не дуже добре, тому що в такому разі виконання задач і обробка запитів веб-сервера будуть “з’їдати” всі ядра, і сайт чи застосунок буде підгальмовувати.
Процес обробки задач можемо побачити на вкладці Tasks. Тут є сортування за станом, часом виконання, можна подивитись самі таски, є трейсбеки (traceback), де відображається код та інформація про помилки, якщо задача впаде.
Flower у нас підключений до брокера і бекенду (Redis), але дані про таски він зберігає в собі. Якщо ми перезавантажимо Redis, то результати виконання поточних задач все рівно будуть зберігатись у процесі Flower до моменту його перезапуску. В той же час, якщо ми перезапустимо Flower, то він обновиться і не буде шукати в Redis, які таски були виконані і які ще чекають на завершення.
Всі параметри, що стосуються виконання задач, можуть бути заздалегідь налаштовані в коді. Настройки в Flower призначені, скоріш, для чогось термінового і для того, щоб тестувальник або ПМ, що зайшов у Flower, міг "загасити пожежу", яка там може відбуватись.
Сьогодні ми познайомились із базовими можливостями Celery і Flower. Celery дозволяє зв’язувати задачі між собою, створювати черги і переносити таски у фонову обробку. А з допомогою веб-інструменту Flower ви будете в курсі виконання задач в реальному часі, зможете відстежувати і перезапускати воркери.
Слідкуйте за нашим блогом, щоб дізнатись більше про технології, які ми використовуємо в роботі над проектами Evergreen. Якщо ж ви маєте готову ідею, шукаєте рішення і команду для її реалізації, напишіть нам.