Введение: Зачем нужны очереди задач?
Представьте, что вы запускаете веб-сервер на Node.js. Пользователь отправляет запрос на отправку 1000 email-писем. Если обрабатывать этот запрос синхронно, сервер "зависнет" на несколько минут — все остальные пользователи не смогут получить ответ.
Как решить эту проблему? Ответ — очереди задач (task queues). Они позволяют отложить выполнение ресурсоёмких операций (отправка писем, генерация PDF, обработка изображений) и выполнять их в фоне, не блокируя основной поток приложения.
В этой статье мы разберём:
- Что такое очередь задач и как она работает
- Популярные библиотеки для Node.js (Bull, Kue)
- Примеры кода: создание, добавление и обработка задач
- Лучшие практики для начинающих
1. Основы очередей задач в Node.js
Что такое очередь задач?
Очередь задач — это структура данных, работающая по принципу FIFO (First In, First Out — первый пришёл, первый ушёл). Задачи добавляются в конец очереди, а обрабатываются из начала. Обычно очереди строятся на базе Redis — быстрого хранилища ключ-значение в памяти.
Основные компоненты очереди:
- Producer — код, который добавляет задачи в очередь
- Consumer (Worker) — код, который забирает задачи и выполняет их
- Queue Manager — механизм, управляющий очередью (обычно Redis)
Популярные библиотеки
Для Node.js существуют две основные библиотеки:
- Bull — современная, быстрая, с поддержкой приоритетов, повторов и задержек
- Kue — старая, но простая библиотека (сейчас менее популярна)
В этой статье мы сосредоточимся на Bull, так как она активно поддерживается и имеет больше возможностей.
2. Установка и настройка Bull
Шаг 1: Установка Redis
Bull использует Redis. Если у вас нет Redis, установите его:
# macOS
brew install redis
# Ubuntu
sudo apt-get install redis-server
# Запуск Redis
redis-server
Шаг 2: Установка Bull
Создайте новый проект и установите пакет:
mkdir queue-demo
cd queue-demo
npm init -y
npm install bull
Шаг 3: Базовая настройка
Создайте файл queue.js:
const Queue = require('bull');
// Создаём очередь с именем 'emailQueue'
const emailQueue = new Queue('emailQueue', {
redis: { host: '127.0.0.1', port: 6379 }
});
module.exports = emailQueue;
3. Пример: очередь для отправки email
Шаг 1: Создаём Producer (добавление задач)
Создайте файл producer.js:
const emailQueue = require('./queue');
async function addEmailTask(to, subject, body) {
// Добавляем задачу в очередь
const job = await emailQueue.add({
to: to,
subject: subject,
body: body
}, {
// Опции задачи
attempts: 3, // Количество попыток при ошибке
backoff: 5000, // Задержка между попытками (5 сек)
delay: 1000 // Задержка перед выполнением (1 сек)
});
console.log(`Задача добавлена с ID: ${job.id}`);
return job;
}
// Пример использования
addEmailTask('user@example.com', 'Привет!', 'Тестовое сообщение');
Шаг 2: Создаём Worker (обработка задач)
Создайте файл worker.js:
const emailQueue = require('./queue');
// Функция, имитирующая отправку email
async function sendEmail(to, subject, body) {
console.log(`Отправка письма на ${to}...`);
// Имитация задержки
await new Promise(resolve => setTimeout(resolve, 2000));
console.log(`Письмо отправлено: ${subject}`);
}
// Обработчик очереди
emailQueue.process(async (job) => {
const { to, subject, body } = job.data;
console.log(`Обрабатываю задачу ${job.id}`);
try {
await sendEmail(to, subject, body);
// Возвращаем результат (опционально)
return { success: true, sentTo: to };
} catch (error) {
// Если ошибка — задача будет повторена (attempts: 3)
throw new Error(`Ошибка отправки: ${error.message}`);
}
});
console.log('Worker запущен и ожидает задачи...');
Шаг 3: Запуск
Запустите worker в одном терминале:
node worker.js
Затем в другом терминале запустите producer:
node producer.js
Вы увидите, как задача добавляется и сразу обрабатывается.
4. Расширенные возможности очередей
Приоритеты задач
Можно задать приоритет (чем меньше число, тем выше приоритет):
await emailQueue.add(data, { priority: 1 }); // Высокий приоритет
await emailQueue.add(data, { priority: 10 }); // Низкий приоритет
Повторные попытки и задержки
Bull автоматически повторяет задачи при ошибках. Настройки:
const job = await emailQueue.add(data, {
attempts: 5, // 5 попыток
backoff: {
type: 'exponential', // Экспоненциальная задержка
delay: 2000 // 2, 4, 8, 16... секунд
}
});
Обработка событий
Можно слушать события очереди:
emailQueue.on('completed', (job, result) => {
console.log(`Задача ${job.id} завершена:`, result);
});
emailQueue.on('failed', (job, err) => {
console.log(`Задача ${job.id} провалена:`, err.message);
});
Планирование задач (повторение)
Bull поддерживает повторяющиеся задачи (как cron):
// Добавляем задачу, которая будет выполняться каждый час
await emailQueue.add(data, {
repeat