Nodejs 求大神揭开谜底!unhandled exception:Error: accept EMFILE

Nodejs 求大神揭开谜底!unhandled exception:Error: accept EMFILE

node.js使用http.request请求别的服务器数据时,有时会报错err:socket hands up ! 当socket hands up 过多时又会报错: unhandled exception:Error: accept EMFILE at errnoException (net.js:769:11) at TCP.onconnection (net.js:1017:24) 直接导致程序必须重启。 求解!设置请求超时时间的时候,当请求超时时候调用abort()或者destroy()释放还是会报错err:socket hands up ! socket hands up是什么原因? 具体代码如下: function(req, res) {

  transtr='*******';
  var options = {
        host : 'xxxxxxxxxxxx',
        port : 80,
        path : '/ajax/requestHead.do',
        data : transtr,
        method : 'POST',
        headers : {
           'Content-Type' : 'application/x-www-form-urlencoded',
           'Content-Length' : transtr.length,
           'Connection':'close'
         }
      };
      console.log(transtr+'----------------------------------------');
      var request_timer = null, serareq = null;

  var serareq = http.request(options, function(serares) {
       var heads = serares.headers;
       serares.setEncoding('utf8');
      clearTimeout(request_timer);

    // 等待响应60秒超时
     var response_timer = setTimeout(function() {
         serares.destroy();
         console.log('Response Timeout.');
      }, 60000);
      console.log("Got response: " + res.statusCode);
      var resData= '';

     serares.on('data', function (respData) {
         resData += respData;
     });

     serares.on('end', function () {
        clearTimeout(response_timer);
        log.logger.info('=======rData============================')
        res.send(resData);
    });
 });
 serareq.on("timeout", function() {
       //res.send('');
        log.logger.info("timeout received");
        if (serareq.serares) {
            serareq.serares.emit("abort");
        }
        serareq.abort();
  });
 //设置请求超时时间5秒
request_timer = setTimeout(function() {
  //serareq.abort();
   serareq.emit("timeout",{message:'have been timeout...'});
    log.logger.info('=====Request Timeout.=================');
 }, 5000);
log.logger.info("Send packageData!!!!!!!!!!!!!!!!");
// write data to request body
serareq.write(transtr);
serareq.end();

}


9 回复

Node.js 中 unhandled exception: Error: accept EMFILE 问题解析

问题描述

当你在 Node.js 中频繁发起 HTTP 请求时,可能会遇到 unhandled exception: Error: accept EMFILE 错误。这通常是因为操作系统限制了每个进程可以打开的文件描述符(file descriptors)数量。当你的 Node.js 应用程序尝试创建超过系统允许的最大文件描述符数时,就会抛出这个错误。

原因分析

  • 文件描述符:每个打开的文件、网络连接等都会占用一个文件描述符。在 Node.js 中,HTTP 请求也会占用文件描述符。
  • 系统限制:不同的操作系统有不同的默认文件描述符限制。例如,在 Linux 上,默认值通常是 1024。

解决方案

要解决这个问题,可以通过以下几种方式:

  1. 增加文件描述符限制

    • 对于 Linux 系统,你可以通过修改 /etc/security/limits.conf 文件来增加每个用户的文件描述符限制。
    * soft nofile 4096
    * hard nofile 4096
    
  2. 优化代码逻辑

    • 确保在完成请求后正确关闭或销毁 HTTP 请求对象。
    • 使用连接池来管理并发请求,避免同时发起过多的请求。
  3. 调整超时处理

    • 在发起请求时设置合理的超时时间,并确保在请求超时时正确地终止请求。

示例代码改进

以下是改进后的代码示例,展示了如何更好地管理请求并防止出现 EMFILE 错误。

const http = require('http');

function(req, res) {
    const transtr = '*******';
    const options = {
        host: 'xxxxxxxxxxxx',
        port: 80,
        path: '/ajax/requestHead.do',
        method: 'POST',
        headers: {
            'Content-Type': 'application/x-www-form-urlencoded',
            'Content-Length': Buffer.byteLength(transtr),
            'Connection': 'close'
        }
    };

    console.log(transtr + '----------------------------------------');

    let request_timer = null;
    let serareq = null;

    serareq = http.request(options, function (serares) {
        const heads = serares.headers;
        serares.setEncoding('utf8');

        // 清除超时定时器
        clearTimeout(request_timer);

        // 等待响应60秒超时
        const response_timer = setTimeout(function () {
            serares.destroy();
            console.log('Response Timeout.');
        }, 60000);

        console.log("Got response: " + serares.statusCode);

        let resData = '';

        serares.on('data', function (chunk) {
            resData += chunk;
        });

        serares.on('end', function () {
            clearTimeout(response_timer);
            log.logger.info('=======rData============================');
            res.send(resData);
        });
    });

    serareq.on("timeout", function () {
        log.logger.info("timeout received");
        if (serareq.serares) {
            serareq.serares.emit("abort");
        }
        serareq.abort();
    });

    // 设置请求超时时间5秒
    request_timer = setTimeout(function () {
        serareq.emit("timeout", { message: 'have been timeout...' });
        log.logger.info('=====Request Timeout.=================');
    }, 5000);

    log.logger.info("Send packageData!!!!!!!!!!!!!!!!");

    // 写入请求体
    serareq.write(transtr);
    serareq.end();
}

关键点总结

  • 文件描述符限制:确保操作系统允许足够的文件描述符。
  • 超时处理:合理设置超时时间,并确保在超时时正确处理请求。
  • 资源释放:在请求完成后及时释放资源,避免占用过多文件描述符。

通过这些方法,你可以有效避免 unhandled exception: Error: accept EMFILE 错误,并提高 Node.js 应用程序的稳定性和性能。


求解啊

open file 數量達上限 默認是1024 你用 ulimit -a看看

// 等待响应60秒超时
     var response_timer = setTimeout(function() {
         serares.destroy();
         console.log('Response Timeout.');
      }, 60000);

这个不应该放在回调里吧。

求解啊…socket hand up是什么原因

有一种情况是response end了之后,继续写入内容

关键socket hand up是什么原因 代码没写错吧?

这个是设置响应超时…是应该放在回调里面的…请求超时和响应超时都写了…

问题分析

EMFILE 错误通常表示 Node.js 进程打开的文件描述符(包括网络连接)超过了系统允许的最大数量。这通常是由于短时间内创建了大量未关闭的 socket 导致的。

解决方案

1. 增加文件描述符限制

你可以通过增加 Node.js 进程的文件描述符限制来解决这个问题。可以通过修改系统配置或启动 Node.js 应用时调整 ulimit 来实现。

例如,在启动 Node.js 应用前,运行以下命令:

ulimit -n 4096

这将把文件描述符限制提高到 4096。

2. 优化代码逻辑

确保在处理完请求后正确关闭 socket,并且在超时后正确销毁 socket。你的代码中已经有一些尝试,但可以进一步优化。

示例代码优化

const http = require('http');

function handleRequest(req, res) {
    const transtr = '*******';
    const options = {
        host: 'xxxxxxxxxxxx',
        port: 80,
        path: '/ajax/requestHead.do',
        method: 'POST',
        headers: {
            'Content-Type': 'application/x-www-form-urlencoded',
            'Content-Length': Buffer.byteLength(transtr),
            'Connection': 'close'
        }
    };

    console.log(transtr + '----------------------------------------');

    const request_timer = setTimeout(() => {
        serareq.abort();
        log.logger.info('=====Request Timeout.=================');
    }, 5000);

    const response_timer = null;

    const serareq = http.request(options, (serares) => {
        clearTimeout(request_timer);
        serares.setEncoding('utf8');

        const resData = '';

        serares.on('data', (respData) => {
            resData += respData;
        });

        serares.on('end', () => {
            res.send(resData);
            log.logger.info('=======rData============================');
        });
    });

    serareq.on("timeout", () => {
        log.logger.info("timeout received");
        serareq.abort();
    });

    serareq.on('error', (err) => {
        log.logger.error('Request error:', err.message);
        res.status(500).send('Internal Server Error');
    });

    serareq.write(transtr);
    serareq.end();
}

const server = http.createServer(handleRequest);
server.listen(3000, () => {
    console.log('Server running on port 3000');
});

总结

通过增加文件描述符限制和优化代码逻辑,可以有效避免 EMFILE 错误。确保在处理完请求后正确关闭 socket,并在超时后正确销毁 socket。

回到顶部