fork()
IPC を使用して子プロセスを作成するノードサーバーがあります。ある時点で、子は長期実行タスクの一部として約10Hzで結果を親に送り返します。渡されたペイロード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');
}
約8kで問題が発生します。たとえば、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
Node v12.7とv12.12の両方で経験。
run()
持っているwhile
ことでループを?それをに切り替えるとsetInterval()
私の問題が解決することを示唆していますか?質問に答えると思います。while
この関数はこのワーカープロセスの唯一の目的であるため、ループを使用します。(小さなIPCペイロードを使用する場合)ループが発生しても問題は発生しませんでした。
setInterval()
、イベントループが解放され、バックグラウンドでI / Oが実行されます。私はそれがこの問題を確実に解決するとは言っていませんが、できるからといって、あなたのやり方で書くのは奇妙な選択のようです。
setTimeout()
またはを使用して、常にノンブロッキングアプローチを優先しsetInterval()
ます。ここでの変更は簡単です。
setInterval()
?