NodeJS IO饿死了,但计时器仍在运行吗?

时间:2019-02-12 10:44:46

标签: javascript node.js io stream named-pipes

我有一个相当复杂的NodeJS应用程序,它可以管理服务器上的各种操作。它在PubNub通道上侦听消息,并向第三方API发出多个请求。它还有一个计时器(使用setTimeout),该计时器每秒运行一次,以检查所有相关进程是否仍在运行并且运行正常。

最近我注意到一些奇怪的行为。在某些情况下(特定的,但100%可复制),它会进入一种奇怪的状态,即所有网络I / O都停止工作。 PubNub更新将停止接收,并且任何发出的HTTP请求(使用request-promise-native库)都将超时。一旦应用程序进入状态,此状态便会显示为永久状态,因为我尝试运行超时时间很长的请求,但它们永远无法解决。

我一直在线阅读有关如何通过使事件循环停止(通常在主线程上使用长时间运行的操作,process.nextTick或对Promise的用法不当)来使IO饿死的可能。我的应用程序中没有这些。

但是,即使很奇怪,我也读过,如果您阻塞事件循环,则所有计时器也会停止工作。我在应用程序内使用setTimeout,setInterval和setIntermediate,即使应用程序已进入此状态,它们都将继续工作。因此,我的事件循环显然仍在运行。

我认为问题很可能是由于我使用命名管道引起的。我的应用程序的一部分创建了一个命名管道,然后将其读取为流:

let pipePath = "/tmp/myPipe";
await promisify(fs.unlink)(pipePath)
            .then(function() {}, function(err) {console.warn("Error removing pipe", err);}); // Ignore errors which may happen if the pipe doesn't exist
childProcess.spawnSync('mkfifo', [pipePath]);

// (not shown) here it starts a 3rd party application that writes to the pipe

let pipeHandle = await promisify(fs.open)(pipePath, O_RDWR); // https://stackoverflow.com/a/53492514/674675

let stream = fs.createReadStream(null, {fd: pipeHandle, autoClose: false});

// (not shown) here several event listeners are added to stream's data event

我还会检测第三方应用程序是否由于某种原因而终止,以及何时清理管道:

// (not-shown) here all the listeners on the stream are removed using removeListener

stream.close(); // This also seems to close the handle to the file

如果第3方应用程序在未写入管道的情况下被杀死,则IO处于冻结状态。但确实很奇怪,只有在第四次之后才进入冻结的IO状态。因此,我可以创建管道,启动第三方应用程序,读取管道并杀死应用程序,然后再将其写入任何数据3次,但是第4次将以某种方式破坏NodeJS。

我正在使用Node v8.10.0。有人知道发生了什么吗?

我尝试过的事情:(没有成功)

我不信任steam.close()关闭文件句柄,所以我尝试了以下替代方法:

// (not-shown) here all the listeners on the stream are removed using removeListener

stream.pause();
await promisify(fs.close)(pipeHandle);

我已更新到Node v11.8.0。

我从使用fs切换到graceful-fs

我弄清楚了如何将Chrome的DevTools连接到该进程,并发现该错误的确切时刻似乎是在创建第五个管道之前调用fs.unlink时。对于fs.unlink的承诺永远不会得到解决或拒绝。仍然不确定为什么会这样,但是至少这是进展。

我尝试用spawnSync("rm", [pipePath])替换fs.unlink调用。这样可以使代码前进到调用之后,但是fs.open操作失败。我还尝试运行fs.openSync,但它冻结了整个程序。因此,似乎在第四个进程被杀死之后,该应用由于某种原因无法再执行IO,并且下一个fs调用将无限期地阻塞。

我逐步执行了该程序,并在另一个屏幕中使用lsof来检查过程中每个有趣部分的文件描述符。通常,在创建管道后,我的临时文件中就有一个文件描述符,我开始读取它,然后关闭流后,描述符就消失了。但是在第四次关闭流之后,描述符仍然存在。这始终是第四次。 4的意义是什么?我开始变得四肢恐惧。

试图将autoClose更改为true。

试图创建一个重现此问题的最小示例。没有完全重现该问题,但也许找到了相关的东西? Question here

使用lslocks来查看进程是否正在等待锁定。 (不是)

切换为使用fifo-js处理管道。这解决了问题!但不幸的是,它会处理通过管道传输的二进制数据,因此它不可用。

0 个答案:

没有答案