如何在Node中的http.request()上设置超时?


92

我试图在使用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();

有什么提示吗?


1
找到了这个答案(它也起作用),但我想知道http.request()stackoverflow.com/questions/6129240/…
Claudio

Answers:


26

只是为了澄清上面答案

现在可以使用timeoutoption和相应的request事件:

// 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.abort();
});

1
不知道这是否与众不同setTimeout(req.abort.bind(req), 3000);
1919年

@AlexanderMills,那么当请求运行正常时,您可能想手动清除超时。
谢尔盖·科瓦连科

4
请注意,严格来说,这是connect超时,一旦建立套接字,它将无效。因此,这对于将套接字保持打开状态太长时间的服务器无济于事(您仍然需要使用setTimeout滚动自己的套接字)。 timeout : A number specifying the socket timeout in milliseconds. This will set the timeout before the socket is connected.
UpTheCreek

这是我在尝试挂​​起连接时要寻找的东西。尝试访问未监听的服务器上的端口时,挂起的连接可能会发生。顺便说一句,API已更改为request.destroyabort已弃用。)此外,这与setTimeout
dturvene

91

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();

为请求分配套接字对象时,将触发“套接字”事件。


确实,这是完全合理的。问题是,现在我无法测试此特定问题(时间过去了...)。所以我现在只能回答答案:)谢谢。
克劳迪奥(Claudio)2012年

别担心。请记住,据我所知,这仅适用于最新版本的节点。我认为我在以前的版本(5.0.3-pre)上进行了测试,但没有触发套接字事件。
罗布·埃文斯

1
处理此问题的另一种方法是使用沼泽标准setTimeout调用。您需要通过以下方式保留setTimeout ID:var id = setTimeout(...); 这样一来,如果您接收到on data等数据,就可以取消它。一个好的方法是将其存储在请求对象本身中,然后在获取一些数据时将其清除为clearTimeout。
罗布·埃文斯

1
您不见了); 在需求结束时。由于不是6个字符,因此无法为您编辑。
JR史密斯

这行得通(谢谢!),但请记住,响应仍将最终到达。req.abort()目前似乎还不是标准功能。因此,在其中socket.on您可能想要设置一个like变量timeout = true,然后在响应处理程序中适当地处理超时
Jonathan Benn

40

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

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

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

参考:Node.js v0.8.8手册和文档


3
“ request.setTimeout”将套接字设置为在套接字上的闲置超时毫秒后超时。我认为这个问题是关于无论活动如何都使请求超时。
ostergaard 2013年

请注意,同样在下面直接使用所涉及的插座中,req.abort()导致错误事件的答案,这应该通过(“错误”)来处理等等
KLoozen

4
request.setTimeout不会中止请求,我们需要在超时回调中手动调用中止。
Udhaya '16

18

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();

3
myTimeout函数在哪里?(编辑:文档说:与绑定到超时事件nodejs.org/api/…相同
Ben Muircroft

请注意,ECONNRESET在两种情况下都可能发生:客户端关闭套接字,服务器关闭连接。要确定它是否是由客户端进行调用abort()有spceial abort事件
基里尔

8

有更简单的方法。

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

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

模块和选项部分:

'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();

如果将以上三个部分全部放在一个文件“ 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!

希望能有所帮助。


2

对我来说-这是一种不太混乱的方式 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 ↑
        });
    });

2

在这里详细说明答案@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()也很好。


1

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

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

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

请求错误事件将被触发

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


添加bind(req)对我没有任何改变。在这种情况下,绑定会做什么?
SpiRail

-1

很好奇,如果改用直线,会发生什么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)

@Claudio您可以更新代码以显示多个请求吗?
onteria_ 2011年

1
当然...有点长,如果不是问题,我会使用paste2.org:paste2.org/p/1448487
Claudio

@Claudio Hmm好的,设置测试环境并编写一些测试代码将要进行,所以我的回复可能会在明天(太平洋时间)以供参考
onteria_ 2011年

@Claudio实际上正在查看您的代码似乎与您的错误不符。据说setTimeout是在一个未定义的值上调用的,但是您调用它的方式是通过全局版本进行的,所以没有办法可以被定义,这让我很困惑。
onteria_ 2011年
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.