Pull to refresh

Про воркеры простыми словами

На работе мне понадобилось реализовать воркер. Описываю, как сам эту тему разобрал. Я не разработчик, пишу утилиты на Python для QA.

Воркер — это сервис, который ждёт событие-триггер и по нему выполняет некий коллбэк. В отличие от фоновых задач в вашем сервисе, воркер живёт в отдельном контейнере.

Пример
Сборщик мусора в БД: пройтись раз в час, например, и удалить старые записи. Или, как у меня задача на работе: обработать xlsx-файл, который передали в ручку.

Зачем
Чтобы сделать работу приложения асинхронной. Представим задачу, которую можно обработать дольше, чем за 10 секунд. Клиент на вашем сайте не будет сидеть и смотреть в экран эти 10 секунд. Он перезагрузит страницу, сессия прервётся, и задача не выполнится. Или веб-сервер вернёт клиенту таймаут. В описанном сценарии обработка запроса — синхронная процедура. Она плохо подходит для быстрых веб-сервисов. А вот асинхронная обработка: кинули запрос, получили ответ 200 OK и пошли чилить, пока задача исполняется — это то, что нужно. Воркер как раз для этого.

Коллбэк
Коллбэком воркера может быть любая нужная функция: отправить имейл, прочитать содержимое, залить файл во временное хранилище и т. д.

Триггеры
Триггерами для воркера могут быть:

  • крон

  • таймер

  • событие

Очереди
Воркеры по событию обычно подписаны на очередь. В моём случае это как раз Redis Queue (библиотека rq, например https://python-rq.org/ ). Запрос в ручку получает 200 OK. Мой сервис создаёт запись в БД типа «задача id такой-то, статус processing» и публикует событие в очереди. Воркер забирает событие, чтобы другие воркеры не могли задачу задублировать, и пробует выполнить свой коллбэк. Если всё ок, воркер пишет в БД данные по выполненной задаче и подтверждает в очереди прочтение события. Иначе воркер может ретраить, может завалить задачу и вернуть её в очередь, а может и сам упасть.

Воркер-пул
Воркеров может быть несколько. Они могут выполнять как несколько разных задач, так и одну вместе. Увеличение числа воркеров требует оркестрации и иногда для этого также выделяет контейнер с оркестратором. Воркеры могут передавать задачи друг другу. Могут конкурировать за задачи, если очередь организовать неправильно. А могут вообще читать разные потоки и быть никак не связаны друг с другом.

Накладные расходы
Чем сложнее слой с воркером, тем больше необходимость следить за их хелсчеками. В отличие от вашего сервиса, воркер может тихо упасть. Ваш сервис отдаёт 200, а по факту задачи не отрабатывают. Так что воркеры накладывают дополнительные накладные расходы: связанность, обработка ошибок, логирование, алерты, ретраи, рестарты подов и т. д.

Образ
Воркер собирается из того же образа, что и ваше приложение, но у него отдельный энтри-поинт. Вместо запуска через main.py у, например, worker.py, есть строчка вида:

def main():

    ... # Какая-то логика по инициализации воркера и очереди

if name == '__main__': 

    main() # Если запускают этот модуль напрямую, выполни команду main()

Из-за этого кода модуль можно вызвать напрямую python -m app.worker. В main(), как правило, скрыта логика какого-то while-true цикла и шатдауна на случай завершения работы воркера.

Tags:
+1
Comments2

Articles