Eu tenho um servidor Nó que cria um processo filho fork()
usando IPC. Em algum momento, a criança envia os resultados de volta aos pais em cerca de 10Hz, como parte de uma tarefa de longa duração. Quando a carga transmitida process.send()
é pequena, tudo funciona bem: todas as mensagens enviadas são recebidas ~ imediatamente e processadas pelos pais.
No entanto, quando a carga útil é 'grande' - não determinei o limite exato de tamanho -, em vez de ser recebida imediatamente pelo pai, todas as cargas são enviadas primeiro e somente depois que o filho termina sua tarefa de longa execução, o pai recebe e processe as mensagens.
tl; dr visual:
Bom (acontece com pequena carga útil):
child: send()
parent: receive()
child: send()
parent: receive()
child: send()
parent: receive()
...
Ruim (acontece com grande carga):
child: send()
child: send()
child: send()
(repeat many times over many seconds)
...
parent: receive()
parent: receive()
parent: receive()
parent: receive()
...
- Isso é um inseto? (Editar: o comportamento ocorre apenas no OS X, não no Windows ou Linux)
- Existe alguma maneira de evitar isso, além de tentar manter minha carga útil do IPC pequena?
Edit 2 : o código de exemplo abaixo usa o contador de tempo e iteração para selecionar quando enviar uma atualização. (No meu código atual, também é possível enviar uma atualização após n iterações ou após o loop alcançar determinados resultados.) Como uma reescrita do código para usar setInterval
/ em setTimeout
vez de um loop é um último recurso para mim, pois exige que eu para remover recursos.
Edit : Aqui está o código de teste que reproduz o problema. No entanto, ele é reproduzido apenas no OS X, não no Windows ou 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');
}
Cerca de 8k o problema acontece. Por exemplo, ao consultar http://localhost:8080/15
vshttp://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
Experiente nos nós v12.7 e v12.12.
run()
tem um while
loop nele? Você está sugerindo que mudar isso para setInterval()
resolverá meu problema? Para responder à pergunta que acho que você está perguntando: uso um while
loop porque essa função é o único objetivo desse processo de trabalho e (com pequenas cargas de IPC) não causou nenhum problema que eu pudesse ver.
setInterval()
libera o loop de eventos para executar E / S em segundo plano. Não estou dizendo que definitivamente resolverá esse problema, mas parece uma escolha estranha escrevê-lo da maneira que você criou, só porque você pode.
setTimeout()
ou setInterval()
. A mudança aqui é trivial.
setInterval()
?