У меня есть Node-сервер, который создает дочерний процесс с fork()
с использованием IPC. В какой-то момент ребенок отправляет результаты родителю на частоте около 10 Гц как часть длительной задачи. Когда полезная нагрузка, переданная в process.send()
, мала, все работает хорошо: каждое отправляемое мной сообщение немедленно принимается и обрабатывается родителем.
Однако, когда полезная нагрузка «большая» - я не определилограничение точного размера - вместо того, чтобы быть немедленно полученным родителем, все полезные данные сначала отправляются, и только после того, как дочерний элемент выполняет свою долгосрочную задачу, родитель получает и обрабатывает сообщения.
tl; dr visual:
Хорошо (происходит с малой полезной нагрузкой):
child: send()
parent: receive()
child: send()
parent: receive()
child: send()
parent: receive()
...
Плохо (происходит с большой полезной нагрузкой):
child: send()
child: send()
child: send()
(repeat many times over many seconds)
...
parent: receive()
parent: receive()
parent: receive()
parent: receive()
...
- Это ошибка? (Изменить: поведение происходит только в OS X, а не в Windows или Linux)
- Есть ли способ избежать этого, кроме попыток сохранить малую полезную нагрузку IPC?
Редактировать 2 : в приведенном ниже примере кода используется время и счетчик итераций для выбора времени отправки обновления. (В моем реальном коде также возможно отправить обновление после n итераций или после того, как цикл достигнет определенных результатов.) Как таковой, переписать код, чтобы использовать setInterval
/ setTimeout
вместоцикл - это последнее средство для меня, так как он требует от меня удаления функций.
Редактировать : Вот тестовый код, который воспроизводит проблему. Однако он воспроизводится только в OS X, а не в Windows или Linux:
server.js
const opts = {stdio:['inherit', 'inherit', 'inherit', 'ipc']};
const child = require('child_process').fork('worker.js', [], opts);
child.on('message', msg => console.log(`parent: receive() ${msg.data.length} bytes`, Date.now()));
require('http').createServer((req, res) => {
console.log(req.url);
const match = /\d+/.exec(req.url);
if (match) {
child.send(match[0]*1);
res.writeHead(200, {'Content-Type':'text/plain'});
res.end(`Sending packets of size ${match[0]}`);
} else {
res.writeHead(404, {'Content-Type':'text/plain'});
res.end('what?');
}
}).listen(8080);
worker.js
if (process.send) process.on('message', msg => run(msg));
function run(messageSize) {
const msg = new Array(messageSize+1).join('x');
let lastUpdate = Date.now();
for (let i=0; i<1e7; ++i) {
const now = Date.now();
if ((now-lastUpdate)>200 || i%5000==0) {
console.log(`worker: send() > ${messageSize} bytes`, now);
process.send({action:'update', data:msg});
lastUpdate = Date.now();
}
Math.sqrt(Math.random());
}
console.log('worker done');
}
Примерно около 8к проблема возникает. Например, при запросе http://localhost:8080/15
против http://localhost:8080/123456
/15
worker: send() > 15 bytes 1571324249029
parent: receive() 15 bytes 1571324249034
worker: send() > 15 bytes 1571324249235
parent: receive() 15 bytes 1571324249235
worker: send() > 15 bytes 1571324249436
parent: receive() 15 bytes 1571324249436
worker done
/123456
worker: send() > 123456 bytes 1571324276973
worker: send() > 123456 bytes 1571324277174
worker: send() > 123456 bytes 1571324277375
child done
parent: receive() 123456 bytes 1571324277391
parent: receive() 123456 bytes 1571324277391
parent: receive() 123456 bytes 1571324277393
Опыт работы на узлах v12.7 и v12.12.