【问题标题】:Close stream when finished完成后关闭流
【发布时间】:2017-01-07 12:59:04
【问题描述】:

我正在使用 fs.createReadStream(path).pipe(res); 使用 Express 流式传输 HTML 5 视频,如果我刷新页面太多次(以千计),最终会导致错误 Error: EMFILE: too many open files。我认为我需要执行某种清理并关闭文件以防止这种情况发生,但我不确定需要做什么。

每当用户寻找客户端未加载的视频部分时,我都会创建一个读取流,这会使此问题在几分钟内发生。任务管理器显示同一个文件打开了数百次,因此它似乎永远不会关闭。

流函数的相关部分:

fs.stat(path, function(err, stats) {
  var size = stats.size;
  var range = req.headers.range;

  if (range) {
    var parts = range.replace(/bytes=/, '').split('-');
    var start = parseInt(parts[0]);
    var end = parts[1] ? parseInt(parts[1]) : size - 1;
    var chunksize = (end - start) + 1;

    res.writeHead(206, {
      'Content-Range': 'bytes ' + start + '-' + end + '/' + size,
      'Accept-Ranges': 'bytes',
      'Content-Length': chunksize,
      'Content-Type': 'video/mp4'
    });

    fs.createReadStream(path, {start: start, end: end}).pipe(res);
  } else {
    res.writeHead(200, {
      'Content-Length': size,
      'Accept-Ranges': 'bytes',
      'Content-Type': 'video/mp4'
    });

    fs.createReadStream(path).pipe(res);
  }
});

【问题讨论】:

  • 你在什么操作系统上运行?苹果? Linux?等等……
  • @PhilPoore Windows,不确定这是否发生在其他操作系统上。

标签: node.js express stream video-streaming html5-video


【解决方案1】:

事实证明,流只会在到达末尾时被销毁并关闭文件 - 这种情况很少发生,因为您必须在不查找或关闭浏览器的情况下加载整个文件。

要解决此问题,您必须侦听响应的关闭事件并销毁流:

res.on('close', function() {
  stream.destroy();
});

有时关闭处理程序永远不会被调用,即使它位于请求函数的第一行。我发现解决这个问题的一个相当老套的方法是在创建流之前检查套接字是否被破坏:

if (req.socket.destroyed) {
  return;
}

固定流功能:

fs.stat(path, function(err, stats) {
  if (req.socket.destroyed) {
    return;
  }

  var stream;
  var size = stats.size;
  var range = req.headers.range;

  if (range) {
    var parts = range.replace(/bytes=/, '').split('-');
    var start = parseInt(parts[0]);
    var end = parts[1] ? parseInt(parts[1]) : size - 1;
    var chunksize = (end - start) + 1;

    res.writeHead(206, {
      'Content-Range': 'bytes ' + start + '-' + end + '/' + size,
      'Accept-Ranges': 'bytes',
      'Content-Length': chunksize,
      'Content-Type': 'video/mp4'
    });

    stream = fs.createReadStream(path, {start: start, end: end});
  } else {
    res.writeHead(200, {
      'Content-Length': size,
      'Accept-Ranges': 'bytes',
      'Content-Type': 'video/mp4'
    });

    stream = fs.createReadStream(path);
  }

  stream.pipe(res);

  res.on('close', function() {
    stream.destroy();
  });
});

【讨论】:

    【解决方案2】:

    在 Linux/Unix 上,EMFILE 问题由ulimit 解决。 我不确定在 Windows 上。 Google Windows ulimit EMFILE 进入这个 Stack Overflow

    https://stackoverflow.com/a/729204/683017

    它说每个进程 2048 个文件描述符是 Windows 的限制。这符合你的说法

    “数以千计”

    如果这个 2048 是一个严格的限制,而我不确定是不是,(您必须四处搜索以了解有关您的特定操作系统的更多信息)。

    如果可能,可以考虑将负载重新平衡到更多进程或更多服务器。

    【讨论】:

    • 我遇到的问题更多的是累积效应。我不太可能同时流式传输多个文件,但在我的应用程序运行时会流式传输许多文件。我只需要弄清楚如何在使用完文件后关闭它们。
    • 在 unix 上,假设它与 windows 类似。文件描述符由操作系统根据需要进行清理。文件描述符有一个最小超时,以停止一遍又一遍地重新打开同一个文件......比如说每个文件至少保持打开 30-60 秒。因此,如果您在一分钟内使用 2048,您将用完并得到您提到的错误。基准测试时经常出现此错误,但在日常使用中很少出现。如果是这样,则表明需要重新安排一些事情以克服 2048 年的限制。
    • 我仔细查看了打开的文件,似乎我可以通过重复流式传输相同的文件打开数百次(这将在几分钟后搜索HTML 5 视频)。
    猜你喜欢
    • 2018-11-21
    • 1970-01-01
    • 2017-02-27
    • 2013-04-22
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多