Node.js中关于accept时EMFILE的处理

更新日期: 2021-06-05阅读: 1.4k标签: node

EMFILE表示进程打开的文件描述符达到了上限,比如建立了一个TCP连接后,调用accept函数的时候就可能触发这个错误。那么这个会导致什么问题呢?首先我们看看Node.js是如何处理连接的。

void uv__server_io(uv_loop_t* loop, uv__io_t* w, unsigned int events) {
  uv_stream_t* stream;
  int err;

  stream = container_of(w, uv_stream_t, io_watcher);
  while (uv__stream_fd(stream) != -1) {
    // 摘取一个TCP连接
    err = uv__accept(uv__stream_fd(stream));
    // 记录下来
    stream->accepted_fd = err;
    // 执行上层回调,回调里消费accepted_fd
    stream->connection_cb(stream, 0);
    // 下一个循环
  }
}

当监听socket上可读事件触发的时候,Node.js就会执行uv server_io进行处理。在uv server_io中Node.js就会不断地调用accept摘取连接,然后执行回调处理该连接。这是正常的流程,那么如果accept出错了,那会怎么样?比如返回了EMFILE错误。 因为Node.js中,epoll的工作模式是水平触发,所以每轮事件循环中,uv__server_io都会被触发,然后执行accept,接着触发错误(如果还没有可用的文件描述符的话)。然而底层已完成三次握手的TCP连接无法得到处理,客户端也只能默默地在等待。Node.js选择的处理策略是关闭连接来通知客户端,服务器已经过载。我们看看Node.js具体是怎么做的。在初始化第一个Libuv stream的时候会首先预留一个文件描述符。

if (loop->emfile_fd == -1) {
    err = uv__open_cloexec("/dev/null", O_RDONLY);
    if (err < 0)
        /* In the rare case that "/dev/null" isn't mounted open "/"
         * instead.
         */
        err = uv__open_cloexec("/", O_RDONLY);
    if (err >= 0)
      loop->emfile_fd = err;
  }

我们看到Node.js打开了一个资源,然后拿到了一个文件描述符保存到emfile_fd。当Node.js处理TCP连接的时候,这个emfile_fd可能就会被用上。

// 摘取TCP连接
    err = uv__accept(uv__stream_fd(stream));
    if (err < 0) {
      // 文件描述符过载
      if (err == UV_EMFILE || err == UV_ENFILE) {
        err = uv__emfile_trick(loop, uv__stream_fd(stream));
        if (err == UV_EAGAIN || err == UV__ERR(EWOULDBLOCK))
          break;
      }

      stream->connection_cb(stream, err);
      continue;
    }

我们看到当uv_accept返回UV_EMFILE错误的时候,会执行uv__emfile_trick。

static int uv__emfile_trick(uv_loop_t* loop, int accept_fd) {
  int err;
  int emfile_fd;

  if (loop->emfile_fd == -1)
    return UV_EMFILE;
  // 关闭预留的文件描述符,下面的uv_accept才能执行成果
  uv__close(loop->emfile_fd);
  loop->emfile_fd = -1;
  // 循环关闭无法处理的TCP连接
  do {
    // 摘取TCP连接
    err = uv__accept(accept_fd);
    if (err >= 0)
      // 关闭TCP连接,通知客户端服务器过载
      uv__close(err);
  } while (err >= 0 || err == UV_EINTR);
  // 重新获取一个预留的文件描述符
  emfile_fd = uv__open_cloexec("/", O_RDONLY);
  if (emfile_fd >= 0)
    loop->emfile_fd = emfile_fd;

  return err;
}

我们看到uv__emfile_trick中关闭了所有无法处理的TCP连接,然后重新补充预留的文件描述符。正常来说uv_accept最后会返回UV_EAGAIN表示没有连接需要处理了,从而结束处理连接的整个逻辑。

参考文章: 如何优雅地处理 accept 出现 EMFILE 的问题
原文 https://zhuanlan.zhihu.com/p/378064533


链接: https://www.fly63.com/article/detial/10390

关于 Node.js 里 ES6 Modules 的一次更新说明

关于 Node.js 里 ES6 Modules 的一次更新说明,总结来说:CommonJS 与 ES6 Modules 之间的关键不同在于代码什么时候知道一个模块的结构和使用它。

用node.js开发一个可交互的命令行应用

在这个教程中,我们会开发一个命令行应用,它可以接收一个 CSV 格式的用户信息文件,教程的内容大纲:“Hello,World”,处理命令行参数,运行时的用户输入,异步网络会话,美化控制台的输出,封装成 shell 命令,JavaScript 之外

Node启动https服务器

首先你需要生成https证书,可以去付费的网站购买或者找一些免费的网站,可能会是key或者crt或者pem结尾的。不同格式之间可以通过OpenSSL转换

nodejs 异步转同步

nodej项目在微信环境开发,nodejs的异步特效,会导致请求没有完成就执行下面的代码,出现错误。经过多方查找,可以使用async模块来异步转同步,只有前一个function执行callback,下一个才会执行。

基于node服务器的大文件(G级)上传

3G的大文件分1500个2M二进度文件,通post方法发送给node服务,服务器全部接收到文件后,进组装生成你上文件。

为什么要把 JavaScript 放到服务器端上运行?

JavaScript比C的开发门槛要低,尽管服务器端JavaScript存在已经很多年了,但是后端部分一直没有市场,JavaScript在浏览器中有广泛的事件驱动方面的应用,考虑到高性能、符合事件驱动、没有历史包袱这3个主要原因,JavaScript成为了Node的实现语言。

了解node.js事件循环

node.js的第一个基本论点是I / O的性能消耗是很昂贵。因此,使用当前编程技术的最大浪费来自于等待I / O完成。有几种方法可以处理性能影响

Node.js 应用:Koa2 使用 JWT 进行鉴权

在前后端分离的开发中,通过 Restful API 进行数据交互时,如果没有对 API 进行保护,那么别人就可以很容易地获取并调用这些 API 进行操作。那么服务器端要如何进行鉴权呢?

Node.js 前端开发指南

我们经常跟Node.js打交道,即使你是一名前端开发人员 -- npm脚本,webpack配置,gulp任务,程序打包 或 运行测试等。即使你真的不需要深入理解这些任务,但有时候你会感到困惑,会因为缺少Node.js的一些核心概念而以非常奇怪的方式来编码。

happypack提升项目构建速度

运行在 Node.js 之上的 Webpack 是单线程模型的,也就是说 Webpack 需要处理的任务需要一件件挨着做,不能多个事情一起做。happypack把任务分解给多个子进程去并发的执行,子进程处理完后再把结果发送给主进程。

点击更多...

内容以共享、参考、研究为目的,不存在任何商业目的。其版权属原作者所有,如有侵权或违规,请与小编联系!情况属实本人将予以删除!