Налаштування розподілених Background Jobs (кілька воркерів)

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

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

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

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

Пропоновані послуги
Показано 1 з 1 послугУсі 2065 послуг
Налаштування розподілених Background Jobs (кілька воркерів)
Складна
~2-3 робочих дні
Часті питання

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

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

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

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

Налаштування розподілених Background Jobs (кілька воркерів)

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

Архітектура

[App Server 1]  [App Server 2]  [App Server 3]
      ↓                ↓               ↓
   dispatch         dispatch        dispatch
      ↓                ↓               ↓
         ┌─────────────────────────────┐
         │     Redis / RabbitMQ        │  ← централізований брокер
         └─────────────────────────────┘
              ↓           ↓         ↓
        [Worker 1]  [Worker 2]  [Worker 3]   ← можуть бути на різних серверах

Брокер — єдиний компонент, який має бути доступний усім серверам. Інші вузли не спілкуються напрямую.

Вимоги до брокера

Redis — стандартний вибір для Laravel. Вимагає phpredis або predis. Для високої доступності — Redis Sentinel або Redis Cluster.

RabbitMQ — підходить для складних сценаріїв маршрутизації (fanout, topic exchanges). Laravel підтримує через пакет vladimir-yuldashev/laravel-queue-rabbitmq.

Amazon SQS — управлений сервіс, не потрібно обслуговувати. Підходить при інфраструктурі на AWS.

Мінімальна конфігурація Redis для production — окремий сервер (не shared з основною БД), persistence включена (appendonly yes), maxmemory-policy налаштована.

Конфігурація Laravel для розподілених воркерів

// config/queue.php
'connections' => [
    'redis' => [
        'driver'       => 'redis',
        'connection'   => 'queue',   // окремий Redis-з'єднання для черг
        'queue'        => env('REDIS_QUEUE', 'default'),
        'retry_after'  => 90,        // секунди до повтору зависшої задачі
        'block_for'    => 5,         // блокуючий BLPOP замість polling
        'after_commit' => true,      // диспетч лише після commit БД
    ],
],

retry_after — ключовий параметр при розподілених воркерах: якщо воркер упав у процесі завдання, завдання буде знову видимо іншим воркерам через retry_after секунд. Повинен бути більше timeout Job'у.

Горизонтальне масштабування через Horizon

Horizon підтримує запуск на кількох серверах. Кожен сервер запускає свій екземпляр Horizon, вони не координуються напрямично — Redis виступає загальним реєстром.

На кожному сервері запускається однаковий Supervisor-конфіг:

[program:horizon]
command=php /var/www/artisan horizon
autostart=true
autorestart=true
user=www-data
stdout_logfile=/var/log/horizon.log
stopwaitsecs=3600

Horizon автоматично балансує воркери всередину одного сервера. Для балансування між серверами — ручне налаштування числа процесів з урахуванням потужності кожного.

Конкурентний доступ та дедупліцирування

При кількох воркерах одне завдання може бути взято двічі, якщо воркер завис та не звільнив блокування. Механізм Redis LPOP атомарен — завдання береться одним воркером. Але «невидимі» завдання (взяті, але не завершені) повертаються у чергу через retry_after.

Якщо завдання повинно виконуватися строго один раз (idempotency) — перевіряємо це явно:

class ProcessPaymentJob implements ShouldQueue
{
    use Dispatchable, InteractsWithQueue, Queueable, SerializesModels;

    public function __construct(private string $paymentId) {}

    public function handle(): void
    {
        // Розподілена блокування через Redis — лише один воркер обробляє платіж
        $lock = Cache::lock("payment:{$this->paymentId}", 120);

        if (!$lock->get()) {
            // Інший воркер вже обробляє
            $this->release(10); // повернути в чергу через 10 секунд
            return;
        }

        try {
            $payment = Payment::find($this->paymentId);

            // Перевірка ідемпотентності
            if ($payment?->status !== 'pending') {
                return; // вже оброблено
            }

            $this->processPayment($payment);
        } finally {
            $lock->release();
        }
    }
}

Cache::lock() використовує Redis SET NX PX — атомарна операція, що гарантує, що ровно один воркер отримає блокування.

Розділення воркерів за типом навантаження

На різних серверах можна запускати воркеры для різних черг, якщо завдання вимагають специфічних ресурсів:

[Server: API-1, API-2]     → воркери для 'critical', 'default'
[Server: Media-1]          → воркери для 'transcoding', 'media'
[Server: Worker-1]         → воркери для 'batch', 'reports', 'low'

На медіа-сервері стоїть GPU або потужний CPU для FFmpeg; на API-серверах — швидкі воркери з малим timeout.

Supervisor на Media-сервері:

[program:media-worker]
command=php /var/www/artisan queue:work --queue=transcoding,media --timeout=3600 --max-jobs=1
numprocs=2
autostart=true
autorestart=true
user=www-data

--max-jobs=1 — воркер берем одне завдання та перезапускається (звільняє память після важкої операції).

Graceful shutdown

При деплоєю потрібно дочекатися завершення поточних завдань, не вбиваючи воркери різко:

php artisan queue:restart

Ця команда ставить флаг у Redis — воркери завершать поточне завдання та зупиняться. Supervisor перезапустить їх з новим кодом.

У Supervisor stopwaitsecs повинен бути не менше максимального timeout завдання:

stopwaitsecs=3600   # для сервера з транскодуванням
stopwaitsecs=120    # для стандартних воркерів

Моніторинг розподіленого стану

Horizon агрегує метрики всіх серверів в одному дашборді. Ключові показники:

  • Throughput (завдань/хвилину) по кожній черзі
  • Wait time — середнє час очікування завдання в черзі
  • Runtime — середнє время виконання
  • Failed jobs — кількість упалих завдань

Автоматичне масштабування воркерів (якщо інфраструктура на Kubernetes):

# HPA для масштабування подів воркерів за метрикою глибини черги
apiVersion: autoscaling/v2
kind: HorizontalPodAutoscaler
metadata:
  name: queue-workers
spec:
  scaleTargetRef:
    apiVersion: apps/v1
    kind: Deployment
    name: queue-worker
  minReplicas: 2
  maxReplicas: 20
  metrics:
    - type: External
      external:
        metric:
          name: redis_queue_depth
          selector:
            matchLabels:
              queue: default
        target:
          type: AverageValue
          averageValue: "50"  # масштабуємо якщо > 50 завдань на воркер

Кастомна метрика redis_queue_depth експортується через Prometheus Redis Exporter.

RabbitMQ як альтернатива

При необхідності складної маршрутизації (різні типи подій → різні черги, fanout трансляція), RabbitMQ дає більше гнучкості:

// config/queue.php
'rabbitmq' => [
    'driver'   => 'rabbitmq',
    'dsn'      => env('RABBITMQ_DSN', 'amqp://user:pass@localhost:5672/'),
    'queue'    => env('RABBITMQ_QUEUE', 'default'),
    'options'  => [
        'exchange' => [
            'name' => 'app-exchange',
            'type' => 'direct',
        ],
        'queue' => [
            'durable'     => true,
            'exclusive'   => false,
            'auto_delete' => false,
        ],
    ],
],

RabbitMQ Management UI (порт 15672) надає детальний моніторинг: consumers, connections, channel-навантаження, message rates.

Сроки

Налаштування Redis Sentinel/Cluster або RabbitMQ, конфігурація Horizon на кількох серверах, Supervisor — 1 робочий день. Розподілені блокування, idempotency-перевірки у критичних Job'ах — 6–8 годин. Інтеграція з Kubernetes HPA та Prometheus — окремий проект на 1–2 дні.