基于nodejs的fs.createReadStream的静态文件服务器不需要考虑文件描述符限制吗?

express.jsserve-statcsend 依赖模块为例,本想学习一下,却完全没有看到处理 EMFILE 异常的逻辑。
stream 体系不会触发这一限制,还是说这一逻辑被底层引擎忽略了,需要业务逻辑框架自行处理?
即便是交由业务逻辑自行处理也很有问题,因为系统必须为静态服务限定一定的资源使用范围,不能说达到上限了抛出异常了事,那其他通常不会出现该问题的逻辑全都会受到株连、“被(静态文件服务器)用光”所有可用的文件描述符。由于是操作系统的限制,所以即便和 nginx 配合使用,自然会对业务逻辑进程造成这种影响。有什么应对的思路?


再者,并发的多个请求针对同一个文件 path 时,fs.createReadStream 会被多次调用,有可能复用吗?如果不能复用,同一 path 的多个 readStream 会累加系统最大打开的文件描述符的占用吗?


另外,如果这一异常发生,是在 fs.createReadStream 函数执行时抛出,还是在 readStream.on("error") 监听中异步抛出?这个异常不太好模拟测试,所以请教一下有经验的大神,谢谢!

阅读 5.2k
2 个回答

从侧面回答一下第二个问题吧,希望不要被踩。

如果是静态文件服务器,为了提高性能建议搞一下缓存处理,也就是说第一次请求的时候,内存的缓存中没有数据,则把文件读到缓存中,下一次请求来的时候,缓存中有数据,则直接走缓存,不要每次都搞文件 I/O,虽然 node.js 支持文件异步 I/O,但是异步操作文件也比内存效率低一些。附上简单的代码,如下:

var http = require('http'),
    fs = require('fs'),
    buf = {};

var root = __dirname + '/' + (process.argv[2] || 'www');

http.createServer(function(req, res) {
  var fileName = root + req.url;
  sendFile(res, fileName);
}).listen(8080);

function sendFile(res, fileName) {
  if(!buf[fileName]) { buf[fileName] = fs.readFileSync(fileName); }

  res.end(buf[fileName]);
}

关于第三个问题,如果异常发生,readStream.on('error')是捕获异常的,不会在这里抛出异常的。关于测试,可以搞一下压力测试,在服务器上多放一些大文件,用 node.js 写个客户端发出 HTTP 请求的脚本,并发请求这些大文件,看看服务程序会不会崩溃。

我的理解中fd数限制在运行时是不好catch的 (类似内存不足,只是后果相对不严重)。更有益的思考方向是如何避免发生,如何减小发生后的损失。

考虑一下那几个文件API的形式: 如果一个fd要供给多个stream,就需要给每个stream管理rewind等状态。所以我猜同一path打开多次会是多个fd。

如果静态文件的访问量已经多到需要考虑这个, 我宁愿先不用nodejs。

如果你想调试 (比如让特定文件第k次打开时失败) 可以试试用ptrace。

撰写回答
你尚未登录,登录后可以
  • 和开发者交流问题的细节
  • 关注并接收问题和回答的更新提醒
  • 参与内容的编辑和改进,让解决方法与时俱进
推荐问题