Налаштування пріоритизації завдань у черзі (Priority Queue)

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

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

Пропоновані послуги
Показано 1 з 1 послугУсі 2065 послуг
Налаштування пріоритизації завдань у черзі (Priority Queue)
Середня
від 1 робочого дня до 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

Налаштування пріоритизації завдань у черзі (Priority Queue)

Всі завдання в одній черзі обробляються в порядку надходження. Це нормально, поки завдання однорідні. Як тільки з'являються завдання з різними вимогами — листи про відновлення пароля не мають чекати в черзі за звітами, які генеруються годину, — потрібна пріоритизація.

Модель пріоритетів

Типовий поділ на три рівні:

Черга Завдання Допустимо очікування
critical Скидання пароля, SMS-коди, сповіщення про платежі < 5 секунд
default Трансакційні листи, сповіщення < 30 секунд
low Звіти, експорт, розсилання, індексація хвилини/години

Реалізація у Laravel (Redis)

Laravel підтримує пріоритети черг через порядок перелічення в --queue:

php artisan queue:work --queue=critical,default,low

Worker спочатку перевіряє critical, якщо пусто — переходить до default, потім до low. Це soft-пріоритет: при наявності завдань у critical завдання в low не обробляються.

Диспетчеризація у конкретну чергу:

// Критична
SendPasswordResetEmail::dispatch($user)->onQueue('critical');

// Звичайний пріоритет
SendWelcomeEmail::dispatch($user)->onQueue('default');

// Низький пріоритет
GenerateMonthlyReport::dispatch($reportId)->onQueue('low');

Або визначте чергу всередину самого Job:

class GenerateMonthlyReport implements ShouldQueue
{
    public string $queue = 'low';
    // ...
}

Horizon: різні пули воркерів для різних пріоритетів

У Horizon створюємо окремі supervisor-пули:

// config/horizon.php
'environments' => [
    'production' => [
        // Пул для критичних завдань — завжди мінімум 2 воркери
        'critical-supervisor' => [
            'connection'     => 'redis',
            'queue'          => ['critical'],
            'balance'        => 'simple',
            'minProcesses'   => 2,
            'maxProcesses'   => 8,
            'timeout'        => 30,
        ],
        // Пул для стандартних завдань — автомасштабування
        'default-supervisor' => [
            'connection'     => 'redis',
            'queue'          => ['default'],
            'balance'        => 'auto',
            'minProcesses'   => 1,
            'maxProcesses'   => 5,
            'timeout'        => 60,
        ],
        // Пул для важких завдань — обмежено
        'low-supervisor' => [
            'connection'     => 'redis',
            'queue'          => ['low'],
            'balance'        => 'simple',
            'processes'      => 2,
            'timeout'        => 3600,
        ],
    ],
],

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

Динамічний пріоритет на основі даних

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

class ProcessUserExportJob implements ShouldQueue
{
    public function __construct(private int $userId) {}

    public function queue(): string
    {
        $user = User::find($this->userId);
        return match(true) {
            $user?->isPremium() => 'default',
            $user?->isEnterprise() => 'critical',
            default => 'low',
        };
    }
}

Проте метод queue() як динамічний у Laravel не підтримується напрямку — пріоритет потрібно визначати при диспетчеризації:

$queue = match(true) {
    $user->isEnterprise() => 'critical',
    $user->isPremium()    => 'default',
    default               => 'low',
};

ProcessUserExportJob::dispatch($user->id)->onQueue($queue);

Пріоритет у BullMQ (Node.js)

BullMQ підтримує числовий пріоритет завдань:

import { Queue } from 'bullmq';

const queue = new Queue('tasks', {
    connection: { host: 'localhost', port: 6379 }
});

// Менше число = вищий пріоритет (1 = найвищий)
await queue.add('send-password-reset', { userId: 123 }, { priority: 1 });
await queue.add('send-welcome-email',  { userId: 456 }, { priority: 5 });
await queue.add('generate-report',     { reportId: 789}, { priority: 10 });

BullMQ використовує Redis Sorted Set для зберігання завдань з пріоритетом — гарантована сортування за пріоритетом всередину однієї черги.

Worker просто забирає завдання в порядку пріоритету:

import { Worker } from 'bullmq';

const worker = new Worker('tasks', async (job) => {
    console.log(`Processing ${job.name} with priority ${job.opts.priority}`);
    // ...
}, { connection: { host: 'localhost', port: 6379 } });

Захист від голодування низкопріоритетних завдань

При великому потоці критичних завдань завдання з low можуть ніколи не обробляватися (starvation). Два підходи:

Aging — завдання збільшує свій пріоритет зі часом очікування. Реалізується через scheduled job, який періодично переглядає чергу:

// Кожні 15 хвилин підвищуємо пріоритет давно чекаючих завдань
Schedule::call(function () {
    $staleJobs = DB::table('jobs')
        ->where('queue', 'low')
        ->where('created_at', '<', now()->subMinutes(30))
        ->get();

    foreach ($staleJobs as $job) {
        DB::table('jobs')
            ->where('id', $job->id)
            ->update(['queue' => 'default']); // підвищуємо до default
    }
})->everyFifteenMinutes();

Виділений воркер для low — один dedicated воркер завжди працює лише з low-чергою, не відволікаючись на critical. Гарантує прогрес навіть при навантаженні:

[program:low-dedicated-worker]
command=php artisan queue:work --queue=low --sleep=5 --timeout=3600
numprocs=1
autostart=true
autorestart=true

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

Важливо стежити за довжиною кожної черги — якщо critical накопичується, це симптом нестачі воркерів:

use Illuminate\Support\Facades\Redis;

$depths = [
    'critical' => Redis::llen('queues:critical'),
    'default'  => Redis::llen('queues:default'),
    'low'      => Redis::llen('queues:low'),
];

// Алерт якщо critical > 50 завдань
if ($depths['critical'] > 50) {
    // сповіщення
}

Horizon показує це в дашборді без додаткового коду.

Сроки

Налаштування трьох черг, розділення існуючих завдань за пріоритетами, Horizon-конфіг пулів — 3–5 годин. Логіка антиголодування, моніторинг глибини, алертинг — ще 2–4 години.