如何在node.js中彻底关闭中断的http连接上的FD

时间:2012-01-01 06:08:47

标签: javascript http node.js

我正在玩节点和(目前)只是尝试通过HTTP从文件系统流式传输一些文件。

我的OS X Lion机器上的Apache Bench(ab)应用程序出现问题,似乎过早地中断了连接。这似乎突出了我的node.js应用程序的问题,如果连接中止,它会泄漏文件句柄。

我把它简化为一个简单的node.js测试用例。当我启动它并对它运行'ab'时,由于打开的文件句柄太多,我最终会得到一个EMFILE异常:

// app.js 

var count = 0;
require('http').createServer(function(req,res){

    console.log('request ' + ++count);

    res.writeHead(200);
    require('fs').createReadStream(
        '/Users/tom/files/8339cdf73594d8f0aab87da123e9e0380723b471'
    ).pipe(res);

}).listen('3000');


$ node app.js
request 1
...
request 317
request 318
request 319

stream.js:105
      throw er; // Unhandled stream error in pipe.
            ^
Error: EMFILE, too many open files '/Users/tom/files/8339cdf73594d8f0aab87da123e9e0380723b471'

我假设发生的事情是HTTP连接被中止但节点的.pipe()机制不知道停止从可读流中读取并关闭FD,所以我试图在HTTP上处理'close'事件ServerRequest并销毁FD,但是当我得到一个错误的FD错误(EBADF)时,似乎仍在读取它:

// app.js

var count = 0;
require('http').createServer(function(req,res){

    console.log('request ' + ++count);

    res.writeHead(200);
    var file = require('fs').createReadStream(
        '/Users/tom/dev/cloudstore2/files/8339cdf73594d8f0aab87da123e9e0380723b471'
    );

    req.on('close', function(){
        console.log('request received close - destroying read FD');
        file.destroy();
    });

    file.pipe(res);

}).listen('3000');

$ node app.js
...
request 112
request 113
request received close - destroying read FD
request received close - destroying read FD
request received close - destroying read FD
request received close - destroying read FD
request received close - destroying read FD
request received close - destroying read FD
request received close - destroying read FD

events.js:48
        throw arguments[1]; // Unhandled 'error' event
                       ^
Error: EBADF, bad file descriptor

是否有'正确'的方法来处理这些中止的HTTP连接并关闭我关联的fs读取流,或者我在node.js'.pipe()处理中看到了错误?


更新 通过在我的文件阅读器上捕获“错误”事件,我可以避免应用程序在此过程中死亡,但这是正确的方法吗?我觉得我错过了一些方法来避免首先出现错误的FD错误。

<snip>
    var file = require('fs').createReadStream(
        '/Users/tom/dev/cloudstore2/files/8339cdf73594d8f0aab87da123e9e0380723b471'
    );

    file.on('error', function(err){
        console.log('error handled in file reader: ' + err);
    });

    req.on('close', function(){
        console.log('request received close - destroying read FD');
        file.destroy();
    });

    file.pipe(res);
</snip>

1 个答案:

答案 0 :(得分:2)

根据nodejs文档

  

<强> http.ServerRequest

     

事件:'关闭'

     

表示之前终止了下层连接   response.end()被调用或能够刷新。

     

就像'结束'一样,每个请求只发生一次此事件,而不再发生   “数据”事件将在事后发生。

     

注意:'close'可以在'end'之后触发,但反之亦然。

我认为end()会杀死管道的目标fh,这意味着如果首先触发结束,则有一个小窗口,管道在关闭后可能会尝试写入请求fh。尝试将file.destroy()调用放在end()事件中。

我试图测试我的想法,但我无法得到错误,所以请告诉我,如果这对您有用。我担心当我向其他用户打开我的应用程序时会遇到这个问题。