Сколько воркеров можно запустить на одну задачу?

Ответ

Оптимальное количество воркеров определяется типом задачи и доступными ресурсами.

Ключевые факторы:

  • CPU-bound задачи (например, обработка изображений, сложные вычисления): количество воркеров не должно превышать количество физических/логических ядер CPU. Иначе возникнет contention и общая производительность упадет.
  • I/O-bound задачи (запросы к БД, внешние API, работа с файловой системой): можно запускать значительно больше воркеров, так как они большую часть времени ожидают ответа от внешних систем. Эмпирическое правило — в 2-3 раза больше количества ядер.
  • Потребление памяти (RAM): Каждый процесс-воркер (например, PHP-FPM worker или процесс, запущенный через pcntl_fork()) потребляет память. Общее потребление не должно превышать доступную RAM.

Практический пример для PHP (обработка очереди):

// Упрощенный пример запуска ограниченного числа воркеров
$maxWorkers = (int) shell_exec('nproc'); // Получаем количество ядер
$queue = getJobsFromQueue();

$pids = [];
for ($i = 0; $i < min($maxWorkers, count($queue)); $i++) {
    $pid = pcntl_fork();
    if ($pid == -1) {
        die('Could not fork');
    } else if ($pid) {
        // Родительский процесс
        $pids[] = $pid;
    } else {
        // Дочерний процесс (воркер)
        processJob($queue[$i]);
        exit(0); // Важно завершить дочерний процесс
    }
}
// Родитель ждет завершения всех воркеров
foreach ($pids as $pid) {
    pcntl_waitpid($pid, $status);
}

В продакшене для управления воркерами используют специализированные системы: Supervisor для PHP-FPM пулов, RabbitMQ Consumer'ы, или очереди задач в Laravel (Horizon), которые сами управляют балансировкой нагрузки.

Ответ 18+ 🔞

Слушай, ну это же классическая история, как с мотором в старой тачке. Можно поставить хоть десять карбюраторов, но если коленвал один — он всё равно будет крутиться с одной скоростью, а бензин хавать как не в себя.

Вот смотри. Всё упирается в то, чем твои воркеры, собственно, занимаются.

CPU-bound задачи — это когда они реально грузят процессор: шифруют что-то, видео кодируют, сложные формулы считают. Тут логика простая, как три копейки: больше воркеров, чем ядер у процессора — смысла ноль ебать. Они начнут драться за ресурсы, как голодные псы за кость, и всё только замедлится. Один воркер на ядро — и всё летает. Больше — уже пизда рулю.

I/O-bound задачи — это когда они в основном тупо ждут: базу данных дергают, по сети что-то качают, файлы читают. Тут процессор большую часть времени спит, так что можно накинуть воркеров овердохуища. Эмпирическое правило — ядра умножить на 2 или 3. Пока один ждёт ответа от базы, другой уже свою порцию работы делает.

Оперативка (RAM) — это вообще отдельная песня. Каждый такой процесс-воркер — он как квартирант: свою койку в памяти занимает. Запустишь их слишком много — система начнёт бздеть и сбрасывать данные на диск (свопить), и тогда всё встанет колом, медленнее смерти.

Вот тебе наглядный пример на PHP, чтобы вообще ни хуя не запутаться:

// Берём столько воркеров, сколько у нас ядер. Больше — уже нахер не надо.
$maxWorkers = (int) shell_exec('nproc');
$queue = getJobsFromQueue();

$pids = [];
for ($i = 0; $i < min($maxWorkers, count($queue)); $i++) {
    $pid = pcntl_fork();
    if ($pid == -1) {
        die('Could not fork');
    } else if ($pid) {
        // Это родительский процесс, он только PID запоминает
        $pids[] = $pid;
    } else {
        // А это уже дочерний, воркер. Он свою работу делает и нахуй уходит.
        processJob($queue[$i]);
        exit(0); // Смотри, не забудь завершиться, а то зомби останутся!
    }
}
// Родитель сидит и ждёт, пока все детишки работу закончат
foreach ($pids as $pid) {
    pcntl_waitpid($pid, $status);
}

А в реальной жизни, конечно, никто вручную этим не страдает. Для этого есть всякие Supervisor, systemd или, в том же Laravel, Horizon. Они за тебя и слезят, и перезапускают упавших, и нагрузку распределяют. Твоя задача — просто правильно число в конфиге выставить, а не изобретать хуй с винтом.