【问题标题】:How to set a timeout on a http.request() in Node?如何在 Node 中的 http.request() 上设置超时?
【发布时间】:2011-09-07 01:53:28
【问题描述】:

我正在尝试在使用 http.request 的 HTTP 客户端上设置超时,但没有成功。到目前为止,我所做的是:

var options = { ... }
var req = http.request(options, function(res) {
  // Usual stuff: on(data), on(end), chunks, etc...
}

/* This does not work TOO MUCH... sometimes the socket is not ready (undefined) expecially on rapid sequences of requests */
req.socket.setTimeout(myTimeout);  
req.socket.on('timeout', function() {
  req.abort();
});

req.write('something');
req.end();

有什么提示吗?

【问题讨论】:

标签: http node.js timeout


【解决方案1】:

只是为了澄清answer above

现在可以使用timeout 选项和相应的请求事件:

// set the desired timeout in options
const options = {
    //...
    timeout: 3000,
};

// create a request
const request = http.request(options, response => {
    // your callback here
});

// use its "timeout" event to abort the request
request.on('timeout', () => {
    request.destroy();
});

【讨论】:

  • 想知道这是否与setTimeout(req.abort.bind(req), 3000); 不同
  • @AlexanderMills,那么当请求正常时,您可能想手动清除超时。
  • 请注意,这严格来说是connect 超时,一旦建立了套接字,它就不起作用了。因此,这对于保持套接字打开时间过长的服务器没有帮助(您仍然需要使用 setTimeout 自行滚动)。 timeout : A number specifying the socket timeout in milliseconds. This will set the timeout before the socket is connected.
  • 这是我在挂起的连接尝试中寻找的。尝试访问未在侦听的服务器上的端口时,可能会发生很多挂起的连接。顺便说一句,API 已更改为 request.destroyabort 已弃用。)此外,这与 setTimeout 不同
  • 正如@dturvene 所说,使用request.destroy 将触发带有ECONNRESET 事件的请求的“错误”处理程序。您也可以将自己的自定义错误传递给request.destroy
【解决方案2】:

您应该将引用传递给请求,如下所示

var options = { ... }
var req = http.request(options, function(res) {
  // Usual stuff: on(data), on(end), chunks, etc...
});

req.setTimeout(60000, function(){
    this.abort();
});
req.write('something');
req.end();

请求错误事件将被触发

req.on("error", function(e){
       console.log("Request Error : "+JSON.stringify(e));
  });

【讨论】:

  • 添加 bind(req) 对我没有任何改变。在这种情况下,bind 做了什么?
  • 恕我直言,我认为这让事情变得更加混乱。使用 req 而不是 this 会使事情变得不那么复杂。
【解决方案3】:

2019 年更新

现在有多种方法可以更优雅地处理这个问题。请在此线程上查看其他一些答案。技术发展迅速,因此答案通常很快就会过时。我的答案仍然有效,但也值得寻找替代方案。

2012 年答案

使用您的代码,问题在于您在尝试在套接字对象上设置内容之前没有等待将套接字分配给请求。这都是异步的:

var options = { ... }
var req = http.request(options, function(res) {
  // Usual stuff: on(data), on(end), chunks, etc...
});

req.on('socket', function (socket) {
    socket.setTimeout(myTimeout);  
    socket.on('timeout', function() {
        req.abort();
    });
});

req.on('error', function(err) {
    if (err.code === "ECONNRESET") {
        console.log("Timeout occurs");
        //specific error treatment
    }
    //other error treatment
});

req.write('something');
req.end();

当请求被分配一个套接字对象时,'socket' 事件被触发。

【讨论】:

  • 这确实说得通。问题是现在我无法测试这个特定的问题(时间过去了......)。所以我现在只能投票赞成这个答案:) 谢谢。
  • 不用担心。请记住,据我所知,这只适用于最新版本的节点。我在以前的版本(5.0.3-pre)上测试过,我认为它没有触发套接字事件。
  • 另一种处理方法是使用沼泽标准 setTimeout 调用。您需要通过以下方式保留 setTimeout id: var id = setTimeout(...);这样您就可以在收到 on 数据等时取消它。一个好方法是将其存储在请求对象本身中,然后如果您获得一些数据则 clearTimeout。
  • 你不见了 );在 req.on 的末尾。由于不是 6 个字符,我无法为您编辑。
  • 你能提一个更优雅的解决方案吗?如果没有更多信息,说有更优雅的解决方案并不是很有帮助
【解决方案4】:

在这里详细说明@douwe 的答案是您可以在http 请求上设置超时。

// TYPICAL REQUEST
var req = https.get(http_options, function (res) {                                                                                                             
    var data = '';                                                                                                                                             

    res.on('data', function (chunk) { data += chunk; });                                                                                                                                                                
    res.on('end', function () {
        if (res.statusCode === 200) { /* do stuff with your data */}
        else { /* Do other codes */}
    });
});       
req.on('error', function (err) { /* More serious connection problems. */ }); 

// TIMEOUT PART
req.setTimeout(1000, function() {                                                                                                                              
    console.log("Server connection timeout (after 1 second)");                                                                                                                  
    req.abort();                                                                                                                                               
});

this.abort() 也可以。

【讨论】:

    【解决方案5】:

    有更简单的方法。

    而不是使用 setTimeout 或直接使用套接字,
    我们可以在客户端使用的“选项”中使用“超时”

    下面是服务端和客户端的代码,分为三部分。

    模块和选项部分:

    'use strict';
    
    // Source: https://github.com/nodejs/node/blob/master/test/parallel/test-http-client-timeout-option.js
    
    const assert = require('assert');
    const http = require('http');
    
    const options = {
        host: '127.0.0.1', // server uses this
        port: 3000, // server uses this
    
        method: 'GET', // client uses this
        path: '/', // client uses this
        timeout: 2000 // client uses this, timesout in 2 seconds if server does not respond in time
    };
    

    服务器部分:

    function startServer() {
        console.log('startServer');
    
        const server = http.createServer();
        server
                .listen(options.port, options.host, function () {
                    console.log('Server listening on http://' + options.host + ':' + options.port);
                    console.log('');
    
                    // server is listening now
                    // so, let's start the client
    
                    startClient();
                });
    }
    

    客户端部分:

    function startClient() {
        console.log('startClient');
    
        const req = http.request(options);
    
        req.on('close', function () {
            console.log("got closed!");
        });
    
        req.on('timeout', function () {
            console.log("timeout! " + (options.timeout / 1000) + " seconds expired");
    
            // Source: https://github.com/nodejs/node/blob/master/test/parallel/test-http-client-timeout-option.js#L27
            req.destroy();
        });
    
        req.on('error', function (e) {
            // Source: https://github.com/nodejs/node/blob/master/lib/_http_outgoing.js#L248
            if (req.connection.destroyed) {
                console.log("got error, req.destroy() was called!");
                return;
            }
    
            console.log("got error! ", e);
        });
    
        // Finish sending the request
        req.end();
    }
    
    
    startServer();
    

    如果将以上3个部分全部放在一个文件“a.js”中,然后运行:

    node a.js
    

    那么,输出将是:

    startServer
    Server listening on http://127.0.0.1:3000
    
    startClient
    timeout! 2 seconds expired
    got closed!
    got error, req.destroy() was called!
    

    希望对您有所帮助。

    【讨论】:

    • 由于 request.abort() 已被弃用,这是我在生产中使用的方法。请注意,如果您将自己的错误传递给 request.destroy(),它将被发送到“错误”处理程序。否则,将向“错误”处理程序发送一个“ECONNRESET”事件。
    【解决方案6】:

    此时有一种方法可以直接在请求对象上执行此操作:

    request.setTimeout(timeout, function() {
        request.abort();
    });
    

    这是绑定到套接字事件然后创建超时的快捷方法。

    参考:Node.js v0.8.8 Manual & Documentation

    【讨论】:

    • request.setTimeout "将套接字设置为在套接字不活动超时毫秒后超时。"我认为这个问题是关于超时请求,无论活动如何。
    • 请注意,与下面直接使用相关套接字的答案相同,req.abort() 会导致错误事件,应由 on('error' ) 等处理。
    • request.setTimeout 不会中止请求,需要在超时回调中手动调用 abort。
    【解决方案7】:

    对我来说 - 这是一种不那么令人困惑的socket.setTimeout

    var request=require('https').get(
        url
       ,function(response){
            var r='';
            response.on('data',function(chunk){
                r+=chunk;
                });
            response.on('end',function(){
                console.dir(r);            //end up here if everything is good!
                });
            }).on('error',function(e){
                console.dir(e.message);    //end up here if the result returns an error
                });
    request.on('error',function(e){
        console.dir(e);                    //end up here if a timeout
        });
    request.on('socket',function(socket){
        socket.setTimeout(1000,function(){
            request.abort();                //causes error event ↑
            });
        });
    

    【讨论】:

      【解决方案8】:

      Rob Evans anwser 对我来说可以正常工作,但是当我使用 request.abort() 时,它会抛出一个未处理的套接字挂起错误。

      我必须为请求对象添加一个错误处理程序:

      var options = { ... }
      var req = http.request(options, function(res) {
        // Usual stuff: on(data), on(end), chunks, etc...
      }
      
      req.on('socket', function (socket) {
          socket.setTimeout(myTimeout);  
          socket.on('timeout', function() {
              req.abort();
          });
      }
      
      req.on('error', function(err) {
          if (err.code === "ECONNRESET") {
              console.log("Timeout occurs");
              //specific error treatment
          }
          //other error treatment
      });
      
      req.write('something');
      req.end();
      

      【讨论】:

      • myTimeout 函数在哪里? (编辑:文档说:与绑定到超时事件 nodejs.org/api/… 相同)
      • 请注意ECONNRESET 可能在两种情况下发生:客户端关闭套接字和服务器关闭连接。要通过调用abort() 来确定它是否是由客户端完成的,有特殊的abort 事件
      【解决方案9】:

      很好奇,如果你直接使用net.sockets 会发生什么?以下是我为测试目的而整理的一些示例代码:

      var net = require('net');
      
      function HttpRequest(host, port, path, method) {
        return {
          headers: [],
          port: 80,
          path: "/",
          method: "GET",
          socket: null,
          _setDefaultHeaders: function() {
      
            this.headers.push(this.method + " " + this.path + " HTTP/1.1");
            this.headers.push("Host: " + this.host);
          },
          SetHeaders: function(headers) {
            for (var i = 0; i < headers.length; i++) {
              this.headers.push(headers[i]);
            }
          },
          WriteHeaders: function() {
            if(this.socket) {
              this.socket.write(this.headers.join("\r\n"));
              this.socket.write("\r\n\r\n"); // to signal headers are complete
            }
          },
          MakeRequest: function(data) {
            if(data) {
              this.socket.write(data);
            }
      
            this.socket.end();
          },
          SetupRequest: function() {
            this.host = host;
      
            if(path) {
              this.path = path;
            }
            if(port) {
              this.port = port;
            }
            if(method) {
              this.method = method;
            }
      
            this._setDefaultHeaders();
      
            this.socket = net.createConnection(this.port, this.host);
          }
        }
      };
      
      var request = HttpRequest("www.somesite.com");
      request.SetupRequest();
      
      request.socket.setTimeout(30000, function(){
        console.error("Connection timed out.");
      });
      
      request.socket.on("data", function(data) {
        console.log(data.toString('utf8'));
      });
      
      request.WriteHeaders();
      request.MakeRequest();
      

      【讨论】:

      • 如果我使用套接字超时,并且我一个接一个地发出两个请求(不等待第一个完成),第二个请求的套接字未定义(至少在我尝试设置超时)..也许套接字上应该有类似 on("ready") 的东西...我不知道。
      • @Claudio 你能更新你的代码来显示多个请求吗?
      • 当然...有点长,如果这不是问题,我使用 paste2.org:paste2.org/p/1448487
      • @Claudio 嗯好的,设置一个测试环境并编写一些测试代码将需要大约,所以我的回复可能会在明天(太平洋时间)的某个时间出现,仅供参考
      • @Claudio 实际上查看您的代码似乎与您的错误不匹配。就是说 setTimeout 是在一个未定义的值上调用的,但是你调用它的方式是通过全局版本,所以不可能是未定义的,让我很困惑。
      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 2014-08-31
      • 2015-10-16
      • 2010-11-19
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2011-04-11
      相关资源
      最近更新 更多