Làm cách nào để đặt thời gian chờ cho http.request () trong Node?


92

Tôi đang cố gắng đặt thời gian chờ trên một ứng dụng HTTP sử dụng http.request mà không gặp may. Cho đến nay những gì tôi đã làm là:

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

Bất kỳ gợi ý?


1
Tìm thấy câu trả lời này (nó hoạt động quá) nhưng tôi tự hỏi, nếu có cái gì đó khác nhau cho http.request () stackoverflow.com/questions/6129240/...
Claudio

Câu trả lời:


26

Chỉ để làm rõ câu trả lời ở trên :

Bây giờ có thể sử dụng timeouttùy chọn và sự kiện yêu cầu tương ứng:

// 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
Wonder nếu điều này là bất kỳ khác nhau hơn là chỉsetTimeout(req.abort.bind(req), 3000);
Alexander Mills

@AlexanderMills, thì bạn có thể muốn xóa thời gian chờ theo cách thủ công, khi yêu cầu hoạt động tốt.
Sergei Kovalenko

4
Lưu ý rằng đây hoàn toàn là connectthời gian chờ, khi ổ cắm được thiết lập thì nó sẽ không có tác dụng. Vì vậy, điều này sẽ không hữu ích với một máy chủ giữ ổ cắm mở quá lâu (bạn vẫn cần phải tự triển khai với setTimeout). timeout : A number specifying the socket timeout in milliseconds. This will set the timeout before the socket is connected.
UpTheCreek,

Đây là những gì tôi đang tìm kiếm trong một nỗ lực kết nối bị treo. Hung kết nối có thể xảy ra một chút khi cố gắng truy cập vào một cổng trên máy chủ không nghe. BTW, API đã thay đổi thành request.destroy( abortkhông được dùng nữa.) Ngoài ra, điều này khác vớisetTimeout
dturvene

91

Cập nhật 2019

Có nhiều cách khác nhau để xử lý việc này một cách thanh lịch hơn. Vui lòng xem một số câu trả lời khác trên chủ đề này. Công nghệ phát triển nhanh nên các câu trả lời thường có thể bị lỗi thời khá nhanh. Câu trả lời của tôi sẽ vẫn hoạt động nhưng nó cũng đáng xem xét các lựa chọn thay thế.

2012 Câu trả lời

Sử dụng mã của bạn, vấn đề là bạn đã không đợi một ổ cắm được gán cho yêu cầu trước khi cố gắng thiết lập nội dung trên đối tượng ổ cắm. Tất cả đều không đồng bộ nên:

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

Sự kiện 'socket' được kích hoạt khi yêu cầu được gán một đối tượng socket.


Điều này thực sự có ý nghĩa. Vấn đề là bây giờ tôi không thể kiểm tra vấn đề cụ thể này (thời gian trôi qua ...). Vì vậy, tôi chỉ có thể ủng hộ câu trả lời cho bây giờ :) Cảm ơn bạn.
Claudio

Đừng lo lắng. Hãy nhớ rằng điều này chỉ hoạt động trên phiên bản mới nhất của nút theo như tôi biết. Tôi đã thử nghiệm trên phiên bản trước (5.0.3-pre) Tôi nghĩ và nó không kích hoạt sự kiện ổ cắm.
Rob Evans

1
Cách khác để xử lý điều này là sử dụng lệnh gọi setTimeout tiêu chuẩn không có thật. Bạn sẽ cần giữ lại id setTimeout với: var id = setTimeout (...); để bạn có thể hủy nó nếu bạn nhận được dữ liệu trên, v.v. Một cách tốt là lưu trữ nó trong chính đối tượng yêu cầu, sau đó clearTimeout nếu bạn nhận được một số dữ liệu.
Rob Evans

1
Bạn đang mất tích); ở cuối req.on. Vì nó không phải là 6 ký tự nên tôi không thể chỉnh sửa nó cho bạn.
JR Smith

Điều này hoạt động (cảm ơn!), Nhưng hãy nhớ rằng cuối cùng phản hồi vẫn sẽ đến. req.abort()dường như không phải là chức năng tiêu chuẩn vào lúc này. Do đó, trong socket.onbạn có thể muốn thiết lập một biến như timeout = truevà sau đó xử lý thời gian chờ một cách thích hợp trong xử lý phản ứng
Jonathan Benn

40

Tại thời điểm này, có một phương pháp để thực hiện việc này trực tiếp trên đối tượng yêu cầu:

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

Đây là một phương thức phím tắt liên kết với sự kiện socket và sau đó tạo thời gian chờ.

Tham khảo: Tài liệu & Hướng dẫn sử dụng Node.js v0.8.8


3
request.setTimeout "đặt socket thành thời gian chờ sau thời gian chờ mili giây không hoạt động trên socket." Tôi nghĩ câu hỏi này là về thời gian thực hiện yêu cầu bất kể hoạt động nào.
ostergaard

Xin lưu ý rằng, giống như trong các câu trả lời dưới đây mà sử dụng ổ cắm có liên quan trực tiếp, các req.abort () gây ra một sự kiện lỗi, cần được xử lý bởi trên ( 'lỗi') vv
KLoozen

4
request.setTimeout sẽ không hủy bỏ yêu cầu, chúng tôi cần gọi hủy bỏ theo cách thủ công trong lệnh gọi lại thời gian chờ.
Udhaya

18

Trình cảm ứng Rob Evans hoạt động chính xác đối với tôi nhưng khi tôi sử dụng request.abort (), nó xảy ra lỗi treo ổ cắm mà vẫn chưa được khắc phục.

Tôi đã phải thêm một trình xử lý lỗi cho đối tượng yêu cầu:

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
đâu là myTimeoutchức năng? (chỉnh sửa: các tài liệu nói: Tương tự như liên kết với các sự kiện thời gian chờ nodejs.org/api/... )
Bến Muircroft

Lưu ý rằng điều đó ECONNRESETcó thể xảy ra trong cả hai trường hợp: máy khách đóng ổ cắm và máy chủ đóng kết nối. Để xác định xem nó có được thực hiện bởi khách hàng hay không bằng cách gọi abort()abortsự kiện quan trọng
Kirill

8

Có một phương pháp đơn giản hơn.

Thay vì sử dụng setTimeout hoặc làm việc trực tiếp với socket,
Chúng tôi có thể sử dụng 'timeout' trong 'option' trong các ứng dụng của khách hàng

Dưới đây là mã của cả máy chủ và máy khách, gồm 3 phần.

Phần mô-đun và tùy chọn:

'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
};

Phần máy chủ:

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

Phần khách hàng:

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

Nếu bạn đặt tất cả 3 phần trên vào một tệp, "a.js", rồi chạy:

node a.js

sau đó, đầu ra sẽ là:

startServer
Server listening on http://127.0.0.1:3000

startClient
timeout! 2 seconds expired
got closed!
got error, req.destroy() was called!

Hy vọng rằng sẽ giúp.


2

Đối với tôi - đây là một cách ít khó hiểu hơn để làm 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

Xây dựng câu trả lời @douwe ở đây là nơi bạn sẽ đặt thời gian chờ cho một yêu cầu 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 () cũng ổn.


1

Bạn nên chuyển tham chiếu đến yêu cầu như bên dưới

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

Sự kiện lỗi yêu cầu sẽ được kích hoạt

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


Thêm ràng buộc (req) không thay đổi bất cứ điều gì đối với tôi. Ràng buộc làm gì trong trường hợp này?
SpiRail

-1

Tò mò, điều gì sẽ xảy ra nếu bạn sử dụng thẳng net.socketsthay thế? Đây là một số mã mẫu tôi đã tổng hợp lại cho mục đích thử nghiệm:

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

Nếu tôi sử dụng thời gian chờ của ổ cắm và tôi đưa ra hai yêu cầu lần lượt (mà không cần đợi yêu cầu đầu tiên kết thúc), thì yêu cầu thứ hai không xác định ổ cắm (ít nhất là tại thời điểm này tôi cố gắng đặt thời gian chờ) .. có lẽ nên có một cái gì đó giống như trên ("sẵn sàng") trên ổ cắm ... Tôi không biết.
Claudio

@Claudio Bạn có thể cập nhật mã của mình để hiển thị nhiều yêu cầu đang được thực hiện không?
onteria_ Ngày

1
Tất nhiên ... nó hơi dài và tôi đã sử dụng paste2.org nếu đây không phải là vấn đề: paste2.org/p/1448487
Claudio

@Claudio Hmm được rồi, việc thiết lập môi trường thử nghiệm và viết một số mã thử nghiệm sẽ mất nhiều thời gian, vì vậy câu trả lời của tôi có thể đến vào lúc nào đó vào ngày mai (Giờ Thái Bình Dương) dưới dạng FYI
onteria_

@Claudio thực sự xem xét mã của bạn dường như không khớp với lỗi của bạn. Người ta nói rằng setTimeout đang được gọi trên một giá trị không xác định, nhưng cách bạn đang gọi nó là thông qua phiên bản toàn cầu, vì vậy không có cách nào có thể không được xác định, khiến tôi khá bối rối.
onteria_ Ngày
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.