Перейти к содержанию

Кластер

v18.x.x

Стабильность: 2 – Стабильная

АПИ является удовлетворительным. Совместимость с NPM имеет высший приоритет и не будет нарушена кроме случаев явной необходимости.

Кластеры процессов Node.js можно использовать для запуска нескольких экземпляров Node.js, которые могут распределять рабочую нагрузку между своими потоками приложений. Если изоляция процессов не требуется, используйте вместо этого модуль worker_threads, который позволяет запускать несколько потоков приложений в рамках одного экземпляра Node.js.

Модуль кластера позволяет легко создавать дочерние процессы, которые совместно используют серверные порты.

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
import cluster from 'node:cluster';
import http from 'node:http';
import { availableParallelism } from 'node:os';
import process from 'node:process';

const numCPUs = availableParallelism();

if (cluster.isPrimary) {
    console.log(`Primary ${process.pid} is running`);

    // Форк рабочих.
    for (let i = 0; i < numCPUs; i++) {
        cluster.fork();
    }

    cluster.on('exit', (worker, code, signal) => {
        console.log(`рабочий ${worker.process.pid} умер`);
    });
} else {
    // Рабочие могут совместно использовать любое TCP-соединение.
    // В данном случае это HTTP-сервер
    http.createServer((req, res) => {
        res.writeHead(200);
        res.end('hello world\n');
    }).listen(8000);

    console.log(`Worker ${process.pid} started`);
}
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
const cluster = require('node:cluster');
const http = require('node:http');
const numCPUs = require('node:os').availableParallelism();
const process = require('node:process');

if (cluster.isPrimary) {
    console.log(`Primary ${process.pid} is running`);

    // Форк рабочих.
    for (let i = 0; i < numCPUs; i++) {
        cluster.fork();
    }

    cluster.on('exit', (worker, code, signal) => {
        console.log(`рабочий ${worker.process.pid} умер`);
    });
} else {
    // Рабочие могут совместно использовать любое TCP-соединение.
    // В данном случае это HTTP-сервер
    http.createServer((req, res) => {
        res.writeHead(200);
        res.end('hello world\n');
    }).listen(8000);

    console.log(`Worker ${process.pid} started`);
}

Запущенный Node.js теперь будет делить порт 8000 между рабочими:

1
2
3
4
5
6
$ node server.js
Primary 3596 is running
Worker 4324 started
Worker 4520 started
Worker 6056 started
Worker 5644 started

В Windows пока невозможно настроить сервер именованных труб в рабочем.

Как это работает

Рабочие процессы порождаются с помощью метода child_process.fork(), чтобы они могли общаться с родителем по IPC и передавать хэндлы сервера туда и обратно.

Кластерный модуль поддерживает два метода распределения входящих соединений.

Первый (и тот, который используется по умолчанию на всех платформах, кроме Windows) - это метод round-robin, когда основной процесс прослушивает порт, принимает новые соединения и распределяет их между рабочими процессами по кругу, с некоторыми встроенными умными функциями, чтобы избежать перегрузки рабочего процесса.

Второй подход заключается в том, что основной процесс создает сокет для прослушивания и отправляет его заинтересованным рабочим. Затем рабочие принимают входящие соединения напрямую.

Теоретически, второй подход должен обеспечивать наилучшую производительность. На практике, однако, распределение имеет тенденцию быть очень несбалансированным из-за капризов планировщика операционной системы. Наблюдались нагрузки, когда более 70% всех соединений оказывались только в двух процессах из восьми.

Поскольку server.listen() передает большую часть работы основному процессу, есть три случая, когда поведение обычного процесса Node.js и рабочего кластера различается:

  1. server.listen({fd: 7}) Поскольку сообщение передается первичному процессу, дескриптор файла 7 в родительском будет прослушан, а хэндл передан рабочему, вместо того, чтобы прослушать представление рабочего о том, на что ссылается дескриптор файла с номером 7.
  2. Если server.listen(handle) явно прослушивать хэндлы, то рабочий будет использовать предоставленный хэндл, а не обращаться к первичному процессу.
  3. server.listen(0) Обычно это заставляет серверы прослушивать случайный порт. Однако в кластере каждый рабочий будет получать один и тот же "случайный" порт каждый раз, когда он выполняет команду listen(0). По сути, порт является случайным в первый раз, но предсказуемым в последующие. Чтобы прослушивать уникальный порт, сгенерируйте номер порта на основе ID рабочего кластера.

Node.js не предоставляет логику маршрутизации. Поэтому важно разработать приложение таким образом, чтобы оно не слишком полагалось на объекты данных в памяти для таких вещей, как сессии и вход в систему.

Поскольку рабочие являются отдельными процессами, они могут быть убиты или перерождены в зависимости от потребностей программы, не затрагивая других рабочих. Пока живы рабочие, сервер будет продолжать принимать соединения. Если ни один рабочий не жив, существующие соединения будут сброшены, а новые соединения будут отклонены. Однако Node.js не управляет количеством рабочих автоматически. Приложение обязано управлять пулом рабочих в соответствии со своими потребностями.

Хотя основным вариантом использования модуля node:cluster является работа в сети, его можно использовать и для других случаев, требующих рабочих процессов.

Класс: Worker

Объект Worker содержит всю публичную информацию и метод о работнике. В первичной системе он может быть получен с помощью cluster.workers. В рабочем он может быть получен с помощью cluster.worker.

Событие: 'disconnect'

Аналогично событию cluster.on('disconnect'), но специфично для этого рабочего.

1
2
3
cluster.fork().on('disconnect', () => {
    // Рабочий отключился
});

Событие: error.

Это событие аналогично событию, предоставляемому child_process.fork().

Внутри рабочего процесса также может использоваться process.on('error').

Событие: exit

  • code <number> Код выхода, если выход произошел нормально.
  • signal <string> Имя сигнала (например, 'SIGHUP'), который вызвал завершение процесса.

Аналогично событию cluster.on('exit'), но специфично для данного рабочего.

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
import cluster from 'node:cluster';

if (cluster.isPrimary) {
    const worker = cluster.fork();
    worker.on('exit', (code, signal) => {
        if (signal) {
            console.log(
                `worker was killed by signal: ${signal}`
            );
        } else if (code !== 0) {
            console.log(
                `рабочий завершился с кодом ошибки: ${code}`
            );
        } else {
            console.log('worker success!');
        }
    });
}
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
const cluster = require('node:cluster');

if (cluster.isPrimary) {
    const worker = cluster.fork();
    worker.on('exit', (code, signal) => {
        if (signal) {
            console.log(
                `worker was killed by signal: ${signal}`
            );
        } else if (code !== 0) {
            console.log(
                `рабочий завершился с кодом ошибки: ${code}`
            );
        } else {
            console.log('worker success!');
        }
    });
}

Событие: listening

Аналогично событию cluster.on('listening'), но специфично для этого рабочего.

1
2
3
cluster.fork().on('listening', (address) => {
    // Worker is listening
});
1
2
3
cluster.fork().on('listening', (address) => {
    // Worker is listening
});

Это не эмитируется в воркере.

Событие: message

  • message <Object>
  • handle {undefined|Object}

Аналогично событию 'message' из cluster, но специфично для этого рабочего.

Внутри рабочего может также использоваться process.on('message').

См. событие process: 'message'.

Вот пример использования системы сообщений. Он ведет подсчет в основном процессе количества HTTP-запросов, полученных рабочими:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
import cluster from 'node:cluster';
import http from 'node:http';
import { availableParallelism } from 'node:os';
import process from 'node:process';

if (cluster.isPrimary) {
    // Отслеживаем http-запросы
    let numReqs = 0;
    setInterval(() => {
        console.log(`numReqs = ${numReqs}`);
    }, 1000);

    // Подсчет запросов
    function messageHandler(msg) {
        if (msg.cmd && msg.cmd === 'notifyRequest') {
            numReqs += 1;
        }
    }

    // Запускаем рабочих и слушаем сообщения, содержащие notifyRequest
    const numCPUs = availableParallelism();
    for (let i = 0; i < numCPUs; i++) {
        cluster.fork();
    }

    for (const id in cluster.workers) {
        cluster.workers[id].on('message', messageHandler);
    }
} else {
    // У рабочих процессов есть http-сервер.
    http.Server((req, res) => {
        res.writeHead(200);
        res.end('hello world\n');

        // Уведомляем первичный процесс о запросе
        process.send({ cmd: 'notifyRequest' });
    }).listen(8000);
}
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
const cluster = require('node:cluster');
const http = require('node:http');
const process = require('node:process');

if (cluster.isPrimary) {
    // Отслеживаем http-запросы
    let numReqs = 0;
    setInterval(() => {
        console.log(`numReqs = ${numReqs}`);
    }, 1000);

    // Подсчет запросов
    function messageHandler(msg) {
        if (msg.cmd && msg.cmd === 'notifyRequest') {
            numReqs += 1;
        }
    }

    // Запускаем рабочих и слушаем сообщения, содержащие notifyRequest
    const numCPUs = require('node:os').availableParallelism();
    for (let i = 0; i < numCPUs; i++) {
        cluster.fork();
    }

    for (const id in cluster.workers) {
        cluster.workers[id].on('message', messageHandler);
    }
} else {
    // У рабочих процессов есть http-сервер.
    http.Server((req, res) => {
        res.writeHead(200);
        res.end('hello world\n');

        // Уведомляем первичный процесс о запросе
        process.send({ cmd: 'notifyRequest' });
    }).listen(8000);
}

Событие: 'online'

Аналогично событию cluster.on('online'), но специфично для этого рабочего.

1
2
3
cluster.fork().on('online', () => {
    // Рабочий находится в сети
});

Это не выдается в воркере.

worker.disconnect()

  • Возвращает: {cluster.Worker} Ссылка на worker.

В рабочем эта функция закроет все серверы, дождется события 'close' на этих серверах, а затем отключит IPC-канал.

В первичном, внутреннее сообщение посылается рабочему, заставляя его вызвать .disconnect() на себя.

Это вызывает установку .exitedAfterDisconnect.

После закрытия сервера он больше не будет принимать новые соединения, но соединения могут быть приняты любым другим прослушивающим рабочим. Существующие соединения будут закрываться обычным образом. Когда соединений больше не будет, см. server.close(), IPC-канал к рабочему будет закрыт, что позволит ему умереть изящно.

Вышесказанное относится только к серверным соединениям, клиентские соединения не закрываются автоматически рабочими, и disconnect не ждет их закрытия перед выходом.

В рабочем, process.disconnect существует, но это не эта функция; это disconnect().

Поскольку долгоживущие соединения с сервером могут блокировать отключение рабочих, может быть полезно посылать сообщение, чтобы можно было предпринять конкретные действия для их закрытия. Также может быть полезно реализовать таймаут, убивающий рабочего, если событие 'disconnect' не было выдано через некоторое время.

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
if (cluster.isPrimary) {
    const worker = cluster.fork();
    let timeout;

    worker.on('listening', (address) => {
        worker.send('shutdown');
        worker.disconnect();
        timeout = setTimeout(() => {
            worker.kill();
        }, 2000);
    });

    worker.on('disconnect', () => {
        clearTimeout(timeout);
    });
} else if (cluster.isWorker) {
    const net = require('node:net');
    const server = net.createServer((socket) => {
        // Connections never end
    });

    server.listen(8000);

    process.on('message', (msg) => {
        if (msg === 'shutdown') {
            // Initiate graceful close of any connections to server
        }
    });
}

worker.exitedAfterDisconnect

Это свойство равно true, если рабочий вышел из системы в результате .disconnect(). Если рабочий вышел другим способом, оно равно false. Если рабочий не вышел, то не определено.

Булево значение worker.exitedAfterDisconnect позволяет отличить добровольный выход от случайного, на основании этого значения первичная система может решить не перезапускать рабочего.

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
cluster.on('exit', (worker, code, signal) => {
    if (worker.exitedAfterDisconnect === true) {
        console.log(
            'О, это было просто добровольно - не стоит беспокоиться'
        );
    }
});

// убить работника
worker.kill();

worker.id

Каждому новому работнику присваивается свой уникальный id, этот id хранится в id.

Пока рабочий жив, это ключ, по которому он индексируется в cluster.workers.

worker.isConnected()

Эта функция возвращает true, если рабочий подключен к своему первичному серверу через его IPC-канал, false в противном случае. Рабочий подключается к своему первичному серверу после его создания. Он отключается после возникновения события 'disconnect'.

worker.isDead()

Эта функция возвращает true, если процесс рабочего завершился (либо из-за выхода, либо из-за получения сигнала). В противном случае она возвращает false.

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
import cluster from 'node:cluster';
import http from 'node:http';
import { availableParallelism } from 'node:os';
import process from 'node:process';

const numCPUs = availableParallelism();

if (cluster.isPrimary) {
    console.log(`Primary ${process.pid} is running`);

    // Форк рабочих.
    for (let i = 0; i < numCPUs; i++) {
        cluster.fork();
    }

    cluster.on('fork', (worker) => {
        console.log('worker is dead:', worker.isDead());
    });

    cluster.on('exit', (worker, code, signal) => {
        console.log('worker is dead:', worker.isDead());
    });
} else {
    // Рабочие могут использовать любое TCP-соединение. В данном случае это HTTP-сервер.
    http.createServer((req, res) => {
        res.writeHead(200);
        res.end(`Текущий процесс\n ${process.pid}`);
        process.kill(process.pid);
    }).listen(8000);
}
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
const cluster = require('node:cluster');
const http = require('node:http');
const numCPUs = require('node:os').availableParallelism();
const process = require('node:process');

if (cluster.isPrimary) {
    console.log(`Primary ${process.pid} is running`);

    // Форк рабочих.
    for (let i = 0; i < numCPUs; i++) {
        cluster.fork();
    }

    cluster.on('fork', (worker) => {
        console.log('worker is dead:', worker.isDead());
    });

    cluster.on('exit', (worker, code, signal) => {
        console.log('worker is dead:', worker.isDead());
    });
} else {
    // Рабочие могут использовать любое TCP-соединение. В данном случае это HTTP-сервер.
    http.createServer((req, res) => {
        res.writeHead(200);
        res.end(`Текущий процесс\n ${process.pid}`);
        process.kill(process.pid);
    }).listen(8000);
}

worker.kill([signal])

  • signal <string> Имя сигнала kill, который нужно послать рабочему процессу. По умолчанию: SIGTERM.

Эта функция убивает рабочий процесс. В основном рабочем она делает это путем отключения worker.process, а после отключения убивает с помощью signal. В рабочем это происходит путем уничтожения процесса с помощью signal.

Функция kill() убивает рабочий процесс, не дожидаясь изящного разъединения, она имеет такое же поведение, как и worker.process.kill().

Для обратной совместимости этот метод называется worker.destroy().

В рабочем процессе существует process.kill(), но это не эта функция, а kill().

worker.process

  • ChildProcess

Все рабочие создаются с помощью child_process.fork(), возвращаемый объект из этой функции хранится как .process. В рабочем хранится глобальный process.

См: Модуль Child Process.

Рабочие процессы будут вызывать process.exit(0), если событие 'disconnect' произойдет на process и .exitedAfterDisconnect не будет true. Это защищает от случайного отключения.

worker.send(message[, sendHandle[, options]][, callback])

  • message <Object>
  • sendHandle Handle
  • options <Object> Аргумент options, если он присутствует, представляет собой объект, используемый для параметризации отправки определенных типов дескрипторов. options поддерживает следующие свойства:
    • keepOpen <boolean> Значение, которое может использоваться при передаче экземпляров net.Socket. Когда true, сокет остается открытым в процессе отправки. По умолчанию: false.
  • callback <Function>
  • Возвращает: <boolean>

Отправка сообщения на рабочий или первичный сервер, опционально с хэндлом.

В первичном случае это отправляет сообщение конкретному рабочему. Она идентична ChildProcess.send().

В рабочем процессе это отправляет сообщение на основной. Это идентично process.send().

В этом примере все сообщения от первичного сервера будут возвращены эхом:

1
2
3
4
5
6
7
8
if (cluster.isPrimary) {
    const worker = cluster.fork();
    worker.send('hi there');
} else if (cluster.isWorker) {
    process.on('message', (msg) => {
        process.send(msg);
    });
}

Событие: разъединение

  • worker {cluster.Worker}

Выдается после отключения IPC-канала рабочего. Это может произойти, когда рабочий изящно завершает работу, его убивают или отключают вручную (например, с помощью worker.disconnect()).

Между событиями 'disconnect' и 'exit' может быть задержка. Эти события могут быть использованы для обнаружения того, что процесс застрял в очистке или что есть долгоживущие соединения.

1
2
3
cluster.on('disconnect', (worker) => {
    console.log(`Рабочий #${worker.id} отключился`);
});

Событие: выход

  • worker {cluster.Worker}
  • code <number> Код выхода, если он вышел нормально.
  • signal <string> Имя сигнала (например, 'SIGHUP'), который вызвал завершение процесса.

Когда любой из рабочих умирает, кластерный модуль выдает событие 'exit'.

Это событие можно использовать для перезапуска рабочего путем повторного вызова .fork().

1
2
3
4
5
6
7
8
cluster.on('exit', (worker, code, signal) => {
    console.log(
        'worker %d died (%s). restarting...',
        worker.process.pid,
        signal || code
    );
    cluster.fork();
});

См. событие child_process: 'exit'.

Событие: fork

  • worker {cluster.Worker}

При форке нового рабочего модуль кластера будет выдавать событие 'fork'. Это событие можно использовать для регистрации активности рабочего и создания пользовательского таймаута.

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
const timeouts = [];
function errorMsg() {
    console.error('Что-то не так с соединением...');
}

cluster.on('fork', (worker) => {
    timeouts[worker.id] = setTimeout(errorMsg, 2000);
});
cluster.on('listening', (worker, address) => {
    clearTimeout(timeouts[worker.id]);
});
cluster.on('exit', (worker, code, signal) => {
    clearTimeout(timeouts[worker.id]);
    errorMsg();
});

Событие: listening

  • worker {cluster.Worker}
  • адрес <Object>

После вызова функции listen() от рабочего, когда событие 'listening' испускается на сервере, событие 'listening' также будет испущено на cluster в первичном.

Обработчик события выполняется с двумя аргументами, worker содержит объект worker, а объект address содержит следующие свойства соединения: address, port и addressType. Это очень полезно, если рабочий прослушивает более одного адреса.

1
2
3
4
5
cluster.on('listening', (worker, address) => {
    console.log(
        `Рабочий теперь подключен к ${адрес.адрес}:${адрес.порт}`
    );
});

Тип addressType является одним из:

  • 4 (TCPv4)
  • 6 (TCPv6)
  • -1 (Unix domain socket)
  • 'udp4`` или'udp6`` (UDPv4 или UDPv6)

Событие: message

  • worker {cluster.Worker}
  • сообщение <Object>
  • handle {undefined|Object}

Выдается, когда основной кластер получает сообщение от любого рабочего.

См. child_process event: 'message'.

Событие: online

  • worker {cluster.Worker}

После форкинга нового рабочего, рабочий должен ответить сообщением online. Когда основной получает сообщение online, он испускает это событие. Разница между 'fork' и 'online' заключается в том, что fork испускается, когда первичный вилкует рабочего, а 'online' испускается, когда рабочий запущен.

1
2
3
4
5
cluster.on('online', (worker) => {
    console.log(
        'Ура, рабочий ответил после того, как его форкнули'
    );
});

Событие: setup

Выдается каждый раз при вызове .setupPrimary().

Объект settings представляет собой объект cluster.settings на момент вызова .setupPrimary() и является только рекомендательным, так как за один такт может быть сделано несколько вызовов .setupPrimary().

Если важна точность, используйте cluster.settings.

cluster.disconnect([callback])

  • callback <Function> Вызывается, когда все рабочие отсоединены и ручки закрыты.

Вызывает .disconnect() для каждого рабочего в cluster.workers.

Когда они будут отключены, все внутренние ручки будут закрыты, что позволит основному процессу изящно завершиться, если не ожидается никакого другого события.

Метод принимает необязательный аргумент обратного вызова, который будет вызван после завершения.

Этот метод может быть вызван только из основного процесса.

cluster.fork([env])

  • env <Object> Пары ключ/значение для добавления в окружение рабочего процесса.
  • Возвращает: {cluster.Worker}

Порождает новый рабочий процесс.

Это может быть вызвано только из основного процесса.

cluster.isMaster

Утративший силу псевдоним для cluster.isPrimary.

cluster.isPrimary

Истина, если процесс является первичным. Это определяется process.env.NODE_UNIQUE_ID. Если process.env.NODE_UNIQUE_ID не определен, то isPrimary будет true.

cluster.isWorker

Истина, если процесс не является основным (это отрицание cluster.isPrimary).

cluster.schedulingPolicy

Политика планирования, либо cluster.SCHED_RR для round-robin, либо cluster.SCHED_NONE, чтобы оставить это на усмотрение операционной системы. Это глобальная настройка и фактически замораживается после порождения первого рабочего или вызова .setupPrimary(), в зависимости от того, что произойдет раньше.

По умолчанию используется SCHED_RR во всех операционных системах, кроме Windows. Windows перейдет на SCHED_RR, когда libuv сможет эффективно распределять ручки IOCP без большого падения производительности.

cluster.schedulingPolicy также может быть задана через переменную окружения NODE_CLUSTER_SCHED_POLICY. Допустимыми значениями являются 'rr' и 'none'.

cluster.settings

  • <Object>
    • execArgv <string[]> Список строковых аргументов, передаваемых исполняемому файлу Node.js. По умолчанию: process.execArgv.
    • exec <string> Путь к рабочему файлу. По умолчанию: process.argv[1].
    • args <string[]> Строковые аргументы, передаваемые рабочему. По умолчанию: process.argv.slice(2).
    • cwd <string> Текущий рабочий каталог рабочего процесса. По умолчанию: undefined (наследуется от родительского процесса).
    • serialization <string> Укажите вид сериализации, используемой для отправки сообщений между процессами. Возможные значения: 'json'' и'advanced''. Подробнее см. в Advanced serialization for child_process. По умолчанию: false.
    • silent <boolean> Посылать ли вывод на родительский stdio. По умолчанию: false.
    • stdio <Array> Настраивает stdio вилочных процессов. Поскольку для работы кластерного модуля используется IPC, эта конфигурация должна содержать запись 'ipc'. Когда эта опция указана, она отменяет silent.
    • uid <number> Устанавливает идентификатор пользователя процесса. (См. setuid(2).)
    • gid <number> Устанавливает групповую идентификацию процесса. (См. setgid(2).)
    • inspectPort {number|Function} Задает инспекторский порт рабочего. Это может быть число или функция, которая не принимает аргументов и возвращает число. По умолчанию каждый рабочий получает свой собственный порт, увеличивающийся от process.debugPort первичного.
    • windowsHide <boolean> Скрыть консольное окно вилочных процессов, которое обычно создается в системах Windows. По умолчанию: false.

После вызова .setupPrimary() (или .fork()) этот объект настроек будет содержать настройки, включая значения по умолчанию.

Этот объект не предназначен для изменения или настройки вручную.

cluster.setupMaster([settings])

Утративший силу псевдоним для .setupPrimary().

cluster.setupPrimary([settings])

setupPrimary используется для изменения поведения "вилки" по умолчанию. После вызова настройки будут присутствовать в cluster.settings.

Любые изменения настроек влияют только на будущие вызовы .fork() и не влияют на уже запущенные рабочие.

Единственный атрибут рабочего, который не может быть установлен через .setupPrimary() - это env, переданный в .fork().

Приведенные выше значения по умолчанию относятся только к первому вызову; значения по умолчанию для последующих вызовов - это текущие значения на момент вызова cluster.setupPrimary().

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
import cluster from 'node:cluster';

cluster.setupPrimary({
    exec: 'worker.js',
    args: ['--use', 'https'],
    silent: true,
});
cluster.fork(); // https worker
cluster.setupPrimary({
    exec: 'worker.js',
    args: ['--use', 'http'],
});
cluster.fork(); // http worker
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
const cluster = require('node:cluster');

cluster.setupPrimary({
    exec: 'worker.js',
    args: ['--use', 'https'],
    silent: true,
});
cluster.fork(); // https worker
cluster.setupPrimary({
    exec: 'worker.js',
    args: ['--use', 'http'],
});
cluster.fork(); // http worker

Это может быть вызвано только из основного процесса.

cluster.worker

Ссылка на текущий объект worker. Недоступно в основном процессе.

1
2
3
4
5
6
7
8
9
import cluster from 'node:cluster';

if (cluster.isPrimary) {
    console.log('Я первичный');
    cluster.fork();
    cluster.fork();
} else if (cluster.isWorker) {
    console.log(`Я рабочий #${cluster.worker.id}`);
}
1
2
3
4
5
6
7
8
9
const cluster = require('node:cluster');

if (cluster.isPrimary) {
    console.log('Я первичный');
    cluster.fork();
    cluster.fork();
} else if (cluster.isWorker) {
    console.log(`Я рабочий #${cluster.worker.id}`);
}

cluster.workers

Хэш, хранящий активные объекты рабочих, с ключом по полю id. Это позволяет легко перебирать всех рабочих. Он доступен только в основном процессе.

Рабочий удаляется из cluster.workers после того, как он отключился и вышел. Порядок между этими двумя событиями не может быть определен заранее. Однако гарантируется, что удаление из списка cluster.workers произойдет до того, как произойдет последнее событие 'disconnect' или 'exit'.

1
2
3
4
5
import cluster from 'node:cluster';

for (const worker of Object.values(cluster.workers)) {
    worker.send('большое объявление всем работникам');
}
1
2
3
4
5
const cluster = require('node:cluster');

for (const worker of Object.values(cluster.workers)) {
    worker.send('большое объявление всем работникам');
}