Паттерны
Управление длительными задачами
Узнайте, как управлять длительными задачами на интервью по System Design
⏱ Паттерн Управление длительными задачами делит API-запросы на два этапа:
мгновенный ответ и фоновое выполнение. Когда пользователь отправляет тяжелую
задачу, например кодирование видео, веб-сервер моментально валидирует запрос,
отправляет задачу в очередь и возвращает jobId за миллисекунды. Тем временем
отдельные воркеры непрерывно опрашивают очередь, берут задачи в работу,
выполняют ресурсоемкие вычисления и фиксируют статус в базе данных.
Проблема
Начнем с постановки проблемы. Представьте, что у вас есть обычный веб-сайт, где пользователи могут просматривать свой профиль. При загрузке страницы профиля сервер делает быстрый запрос в базу данных для получения информации. Весь процесс занимает менее 100 миллисекунд. Пользователь кликает и мгновенно видит свои данные. Все отлично.
Теперь представим, что нам нужно сгенерировать PDF-отчет о годовой активности пользователя. Для этого нужно прочитать несколько таблиц, агрегировать данные по миллионам строк, отрисовать графики и собрать готовый документ. Весь процесс занимает как минимум 45 секунд.
При синхронной обработке браузер пользователя будет висеть в ожидании все эти 45 секунд. Большинство балансировщиков нагрузки и веб-серверов имеют ограничения по таймауту около 30-60 секунд, поэтому запрос может даже не завершиться. Но даже в случае успеха пользовательский опыт оставляет желать лучшего. Человек просто смотрит на индикатор загрузки и не понимает, происходит ли какой-то прогресс.
Генерация PDF - не единственный пример. Загрузка видео требует транскодирования, на которое уходят минуты. Загрузка фото профиля нуждается в изменении размера, обрезке и создании нескольких превью. Массовые операции, такие как рассылка писем тысячам пользователей или импорт больших CSV-файлов, занимают еще больше времени.
Даже если бы мы могли бесконечно масштабировать систему, синхронная обработка приводит к плохому пользовательскому опыту. Человек нажимает "Сгенерировать отчет" и дальше ничего не происходит. Работает ли система? Случилась ли ошибка? Стоит ли обновить страницу? Прождав 30 секунд, многие решат, что что-то сломалось, и попробуют снова. Это создаст дублирующуюся нагрузку, которая только усугубит ситуацию.
Синхронная обработка имеет определенные ограничения, когда операция занимает больше нескольких секунд. В чем же альтернатива?
Решение
Вместо того чтобы заставлять пользователя ждать, пока мы соберем PDF-отчет, мы разбиваем операцию на две части. При нажатии на кнопку мы немедленно сохраняем запрос в очередь и отвечаем: "Мы генерируем ваш отчет. Вы получите уведомление, когда он будет готов". Это занимает миллисекунды.
Задача веб-сервера сильно упрощается. Он лишь валидирует запрос, помещает его в
очередь и возвращает jobId. Сама генерация PDF происходит совсем в другом
месте.
Это и есть паттерн Управление длительными задачами. Главная идея - отделить прием запроса от его выполнения. Веб-серверы становятся легкими маршрутизаторами: они быстро подтверждают получение задачи и передают ее дальше. Отдельный пул воркеров выполняет тяжелую часть работы. Они забирают задачи из общей очереди, обрабатывают их в своем темпе и обновляют статус после завершения. Исходный HTTP-запрос завершается сразу, без ожидания результата.
Очередь работает как буфер между веб-серверами и воркерами. Она надежно хранит задачи, поэтому мы ничего не потеряем при падении воркера. Популярные варианты - Redis(с Bull или BullMQ), AWS SQS, RabbitMQ или Kafka для серьезных нагрузок. Очередь также позволяет видеть сколько задач накопилось, как долго они обрабатываются и есть ли ошибки при их выполнении.
Такое разделение решает и проблему масштабирования. Веб-серверам не нужны дорогие GPU только из-за того, что часть запросов связана с обработкой видео. Можно держать веб-серверы на недорогих машинах, а для воркеров использовать машины с GPU. Если в конце месяца скапливается очередь на генерацию отчетов, мы просто запускаем дополнительные воркеры, не трогая веб-серверы. Каждый компонент масштабируется под свою собственную нагрузку.
Пользовательский опыт тоже становится заметно лучше. Вместо зависшего браузера пользователи получают мгновенное подтверждение. Они могут проверить свою позицию в очереди или статус обработки позже. Когда задача выполнена, мы уведомляем их через email, push-уведомление или обновление по WebSocket. Можно уйти с сайта, закрыть ноутбук и вернуться уже за готовым результатом.
Этот подход применим к любой операции, занимающей больше нескольких секунд. Обработка изображений, транскодирование видео, импорт данных, запросы к сторонним API с жесткими ограничениями частоты, генерация отчетов, email-рассылки - все это выигрывает от асинхронного подхода. Конкретная технология очереди и способ реализации воркеров могут отличаться, но базовый паттерн всегда один и тот же: быстро принять запрос, обработать асинхронно, уведомить после завершения.
Перейдите на Premium, чтобы продолжить
Разблокируйте доступ к этой статье и всем остальным материалам с NowInterview Premium
Перейти на Premium