Реалізація черги завдань парсингу (Redis/RabbitMQ/BullMQ)

Наша компанія займається розробкою, підтримкою та обслуговуванням сайтів будь-якої складності. Від простих односторінкових сайтів до масштабних кластерних систем, побудованих на мікро сервісах. Досвід розробників підтверджено сертифікатами від вендорів.

Розробка та обслуговування будь-яких видів сайтів:

Інформаційні сайти або веб-програми
Сайти візитки, landing page, корпоративні сайти, онлайн каталоги, квіз, промо-сайти, блоги, ресурси новин, інформаційні портали, форуми, агрегатори
Сайти або веб-програми електронної комерції
Інтернет-магазини, B2B-портали, маркетплейси, онлайн-обмінники, кешбек-сайти, біржі, дропшиппінг-платформи, парсери товарів
Веб-програми для управління бізнес-процесами
CRM-системи, ERP-системи, корпоративні портали, системи управління виробництвом, парсери інформації
Сайти або веб-програми електронних послуг
Дошки оголошень, онлайн-школи, онлайн-кінотеатри, конструктори сайтів, портали надання електронних послуг, відеохостинги, тематичні портали

Це лише деякі з технічних типів сайтів, з якими ми працюємо, і кожен із них може мати свої специфічні особливості та функціональність, а також бути адаптованим під конкретні потреби та цілі клієнта.

Послуги, які ми пропонуємо
Показано 1 з 1Усі 2062 послуг
Реалізація черги завдань парсингу (Redis/RabbitMQ/BullMQ)
Середній
~3-5 днів
Часті запитання

Наші компетенції:

Етапи розробки

Останні роботи

  • image_website-b2b-advance_0.webp
    Розробка сайту компанії B2B ADVANCE
    1361
  • image_web-applications_feedme_466_0.webp
    Розробка веб-додатків для компанії FEEDME
    1252
  • image_websites_belfingroup_462_0.webp
    Розробка веб-сайту для компанії БЕЛФІНГРУП
    958
  • image_ecommerce_furnoro_435_0.webp
    Розробка інтернет магазину для компанії FURNORO
    1190
  • image_crm_enviok_479_0.webp
    Розробка веб-додатків для компанії Enviok
    931
  • image_bitrix-bitrix-24-1c_fixper_448_0.webp
    Розробка веб-сайту для компанії ФІКСПЕР
    949

Ми не раз бачили, як парсинг у циклі розвалюється при першій же помилці мережі. Пропадають тисячі рядків, а налагодження займає години. Черга завдань вирішує три фундаментальні проблеми: ізоляція збоїв, автоматичні повторні спроби та горизонтальне масштабування воркерів. В одному з проектів з 50 000 сторінок каталогу ми перейшли з лінійного скрипта на BullMQ — час виконання скоротився вдвічі, а відсоток втрачених даних впав до нуля. Середня затримка відновлення після збою становить 60 секунд завдяки експоненціальним ретраям.

Яку проблему вирішує черга?

Замість послідовного обходу ви ставите завдання в чергу і забуваєте. Якщо воркер впав, завдання повертається в чергу і повторюється з експоненціальною затримкою. Потоки паралельної обробки налаштовуються через concurrency, а при зростанні навантаження додаються нові інстанси. Типова конфігурація для середнього проекту — 5–10 воркерів з concurrency 5, що дає до 50 одночасних завдань.

Як вибрати брокер черги?

Для більшості веб-проектів оптимальні BullMQ або Celery. BullMQ працює на Redis і надає UI Board для моніторингу, підтримує пріоритети та до 100 тисяч завдань на добу на одному інстансі. Celery краще підходить для Python-стеку: ланцюжки завдань та групові обробки будуються без додаткового коду. RabbitMQ виправданий у високонавантажених системах, де потрібна складна маршрутизація через routing keys та гарантована доставка на рівні AMQP. Наприклад, при агрегації даних з 20+ джерел з різною швидкістю. Офіційна документація RabbitMQ рекомендує DLQ для критичних даних.

Порівняйте характеристики:

Характеристика BullMQ Celery RabbitMQ
Бекенд Redis Redis/RabbitMQ AMQP
Макс. throughput ~100k/добу ~50k/добу >200k/добу (кластер)
Вбудований UI Так (Board) Flower Так (Management)
Складність налаштування Низька Середня Висока

BullMQ: налаштування воркера та ретраї

import { Queue, Worker, Job } from 'bullmq';
import { Redis } from 'ioredis';

const connection = new Redis({ host: 'localhost', port: 6379, maxRetriesPerRequest: null });

// Створення черги
export const scrapeQueue = new Queue('scraping', {
  connection,
  defaultJobOptions: {
    attempts: 3,
    backoff: { type: 'exponential', delay: 60_000 },
    removeOnComplete: { count: 500 },
    removeOnFail: { count: 200 },
  },
});

// Додавання завдання
await scrapeQueue.add('scrape-url', {
  url: 'https://example.com/catalog?page=5',
  siteId: 42,
  depth: 1,
}, { priority: 1 });

// Воркер
const worker = new Worker('scraping', async (job: Job) => {
  const { url, siteId } = job.data;
  const html = await fetchWithProxy(url);
  const products = parseProducts(html);
  await saveProducts(products, siteId);
  return { count: products.length };
}, { connection, concurrency: 5 });

worker.on('failed', (job, err) => {
  logger.error(`Job ${job?.id} failed: ${err.message}`);
});

Celery: пайплайн з ланцюжками завдань

from celery import Celery, chain, chord
import redis

app = Celery('scraper', broker='redis://localhost:6379/0',
             backend='redis://localhost:6379/1')

app.conf.task_routes = {
    'scraper.tasks.fetch_listing': {'queue': 'listings'},
    'scraper.tasks.fetch_product': {'queue': 'products'},
}

@app.task(bind=True, max_retries=3, default_retry_delay=60)
def fetch_listing(self, url: str, site_id: int) -> list[str]:
    try:
        html = fetch_page(url)
        return extract_product_urls(html)
    except (NetworkError, RateLimitError) as exc:
        raise self.retry(exc=exc, countdown=2 ** self.request.retries * 60)

@app.task(bind=True, max_retries=3)
def fetch_product(self, url: str, site_id: int) -> dict:
    try:
        html = fetch_page(url)
        return parse_product(html)
    except Exception as exc:
        raise self.retry(exc=exc)

@app.task
def save_products(products: list[dict], site_id: int):
    bulk_upsert(products, site_id)

# Запуск пайплайну
def start_site_crawl(site_id: int, catalog_url: str):
    urls = fetch_listing.delay(catalog_url, site_id).get()
    chord(
        fetch_product.s(url, site_id) for url in urls
    )(save_products.s(site_id))
Приклад конфігурації Celery з rate limiting
app.conf.task_annotations = {
    'scraper.tasks.fetch_product': {
        'rate_limit': '10/m'
    }
}

Це обмежує кількість завдань fetch_product до 10 на хвилину на воркер, що допомагає уникнути блокування за IP.

Dead Letter Queue: налаштування та аналіз

Завдання, які вичерпали всі спроби, потрапляють у Dead Letter Queue. Це не просто сміттєвий кошик — це черга для ручного аналізу та переробки. У RabbitMQ DLQ налаштовується через аргументи черги:

channel.queue_declare(
    queue='scraping.products',
    durable=True,
    arguments={
        'x-dead-letter-exchange': 'scraping.dlx',
        'x-dead-letter-routing-key': 'failed',
        'x-message-ttl': 3600000,  # 1 година
    }
)
channel.exchange_declare(exchange='scraping.dlx', exchange_type='direct')
channel.queue_declare(queue='scraping.failed', durable=True)
channel.queue_bind(queue='scraping.failed', exchange='scraping.dlx', routing_key='failed')

Завдання з DLQ можна повторно відправити в основну чергу після усунення причини збою — через Admin UI або скрипт. У BullMQ DLQ реалізується за допомогою окремої черги та обробника failed.

Порівняння стратегій повторних спроб

Стратегія Затримка Коли використовувати
Експоненціальна 2^retry * base При тимчасових помилках мережі
Лінійна retry * base При rate limiting
Постійна fixed delay При стабільних умовах

Моніторинг черги

BullMQ Board (UI для BullMQ) або Flower (для Celery) дають візуальне представлення про стан черг. Ключові метрики, які варто відстежувати:

  • Глибина черги (waiting jobs)
  • Швидкість обробки (jobs/sec)
  • Відсоток помилок за типами завдань
  • Час виконання (p50, p95, p99)

Ці метрики експортуються в Prometheus через /metrics ендпоінт та візуалізуються в Grafana. Середній час відповіді наших парсерів після впровадження моніторингу знизився на 35%.

Процес роботи

  1. Аналітика: визначаємо обсяг даних, частоту парсингу, вимоги до надійності.
  2. Проектування: обираємо брокер, схему завдань, налаштування ретраїв.
  3. Реалізація: пишемо воркери, DLQ, інтеграція з моніторингом.
  4. Тестування: прогоняємо навантаження, перевіряємо поведінку при збоях.
  5. Деплой: розгортаємо на сервері або в kubernetes, підключаємо CI/CD.

Що входить у роботу

  • Налаштування черги (BullMQ/Celery/RabbitMQ) з політикою ретраїв та DLQ
  • Інтеграція з Redis (Sentinel/Cluster) або RabbitMQ
  • Моніторинг (Prometheus + Grafana) та алерти
  • Документація з експлуатації та навчання команди
  • Гарантія працездатності протягом місяця після здачі

Ми займаємося парсинговими системами понад 6 років, реалізували більше 30 проектів з чергами. Отримайте консультацію інженера — ми оцінимо ваш проект за один день і запропонуємо оптимальну архітектуру.

Строки реалізації

Базова черга з ретраями та DLQ — 3–4 робочих дні. Додавання метрик, UI та кластеризації — ще 2–3 дні. Підсумкова вартість розраховується індивідуально під ваш обсяг даних.

Зв'яжіться з нами — ми запропонуємо рішення під вашу задачу. Економія на повторних помилках може досягати 40% від бюджету на парсинг.

Послуги бекенд-розробки: production-grade надійність

На production-сервері о 3:14 ночі черга Laravel Jobs перестала оброблятися — 40 000 необроблених завдань у Redis. Причина: worker упав через memory leak у статичній змінній Eloquent observer, supervisor не перезапустив через misconfigured stopwaitsecs. Ми розбирали такий інцидент на проекті з 500 RPS: діагностика 4 години, фікс — 20 хвилин. Щоб ви не втрачали гроші, пропонуємо послуги бекенд-розробки з акцентом на production-grade надійність — 10+ років досвіду, 50+ проектів, 5 років на ринку. Оцінимо ваш проект за 2 дні.

Які проблеми вирішуємо

N+1 запити: головний вбивця швидкості

N+1 — найпоширеніша причина повільних сторінок у Laravel-додатках. Стандартна історія: сторінка працювала нормально на dev з 10 записами, на production з 10 000 — 8-секундне завантаження.

Laravel Debugbar у dev-оточенні показує кількість запитів. Більше 20 — сигнал для audit.

Model::preventLazyLoading(! app()->isProduction());

Telescope для профілювання: логує всі запити, jobs, mail, notifications з деталізацією. Після впровадження eager loading час завантаження сторінки падає з 8 с до 0.3 с — у 27 разів.

Memory leak у статичних змінних

У Laravel Octane або Swoole додаток тримається в пам’яті між запитами. Статичні змінні не скидаються — призводять до неконтрольованого росту пам’яті. Використовуємо defer-функції та контейнерні біндинги для коректного скидання стану.

Неправильний connection pool

Rails, Laravel, Django відкривають нове з'єднання PostgreSQL на кожен PHP/Python процес. 100 воркерів — 100 з'єднань. PostgreSQL деградує від 200+ активних з'єднань через overhead на управління.

PgBouncer у transaction pooling: 1000 воркерів → 20–50 реальних з'єднань. Це знижує latency на 40% та зменшує витрати на хостинг на 30% — при середній вартості хостингу $2,000/міс економить $600/міс. GIN-індекс для JSONB до 100 разів швидший за B-tree при пошуку.

Як Octane справляється з високим навантаженням?

Laravel Octane (RoadRunner або Swoole) прибирає overhead bootstrap на кожен HTTP-запит. Приріст: 3–8x на синтетичних бенчмарках, 2–4x на реальних додатках. Важливо: не зберігати стан у статичних змінних — застосовуємо це на проектах >1000 RPS.

Як PostgreSQL допомагає уникнути повільних запитів?

Використовуємо composite indexes для WHERE + ORDER BY, partial indexes для фільтрів з високою селективністю, GIN-індекси для JSONB та full-text search. to_tsvector + GIN замість LIKE '%query%' — запобігає seq scan навіть на мільйонах записів. Аналізуємо плани через EXPLAIN ANALYZE та pg_stat_statements.

Як обрати стек для вашого проекту?

Стек Коли використовувати
Laravel + Octane CRUD, бізнес-логіка, REST/GraphQL API, адмінки
Node.js (Fastify) Realtime WebSocket, streaming, serverless, висока I/O concurrency
Go Високонавантажені мікросервіси (>10k RPS), gRPC, DevOps-інструменти
Django + DRF ML-пайплайни, інтеграція з AI, складна обробка даних
Ruby on Rails Швидкий MVP з багатим екосистемою гемів

Node.js виправданий для realtime: Laravel публікує події в Redis Pub/Sub, Node.js підписується та транслює клієнтам. Go — для goroutines (10k з'єднань на сервер — норма), але розробка повільніша, ніж Laravel.

Чому Redis критичний для продуктивності?

Redis виконує кілька ролей:

Роль Деталі
Кеш Кешування результатів важких запитів, фрагментів HTML
Черги Backend для Laravel Queue / Celery
Session store Distributed sessions в multi-instance оточенні
Pub/Sub Realtime події між сервісами
Rate limiting Sliding window counters для API throttling
Leaderboards Sorted Sets для рейтингів

Redis Cluster для горизонтального масштабування, Sentinel для автоматичного failover. Замовте консультацію щодо оптимізації Redis для вашого проекту.

Що входить в роботу під ключ

  • Архітектурне проектування (документація API, схема БД, діаграма сервісів)
  • Реалізація за узгодженим ТЗ з code review
  • Налаштування CI/CD (GitHub Actions, Docker), моніторингу (Sentry, Grafana), алертингу
  • Навантажувальне тестування (k6, wrk) зі звітом
  • Передача вихідних кодів, доступів, інструкція з деплою
  • Навчання команди замовника (2–3 сесії)
  • Гарантійна підтримка 1 місяць після здачі

Орієнтири по термінах

Задача Термін
REST API для мобільного/SPA (середня складність) 6–12 тижнів
Backend зі складною бізнес-логікою + інтеграції 12–20 тижнів
Високонавантажений сервіс на Go 8–16 тижнів
Міграція legacy PHP на Laravel 16–32 тижні

Вартість розраховується індивідуально після аналізу вимог до навантаження, інтеграцій та бізнес-логіки. Зв'яжіться з нами для безкоштовного аудиту вашого поточного backend — отримайте план оптимізації за 2 дні. Замовте консультацію та дізнайтеся, як знизити витрати на інфраструктуру на 30% без втрати продуктивності.