จะกำหนดระยะหมดเวลาของ http.request () ใน Node ได้อย่างไร?


94

ฉันกำลังพยายามกำหนดระยะหมดเวลาบนไคลเอนต์ HTTP ที่ใช้ http.request โดยไม่ต้องมีโชค จนถึงตอนนี้สิ่งที่ฉันทำคือ:

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

คำตอบ:


28

เพียงเพื่อชี้แจงคำตอบด้านบน :

ตอนนี้สามารถใช้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.abort();
});

1
สงสัยว่าสิ่งนี้จะแตกต่างไปจากเดิมหรือไม่setTimeout(req.abort.bind(req), 3000);
Alexander Mills

@AlexanderMills คุณอาจต้องการล้างการหมดเวลาด้วยตนเองเมื่อคำขอทำงานได้ดี
Sergei Kovalenko

4
โปรดทราบว่านี่เป็นการconnectหมดเวลาอย่างเคร่งครัดเมื่อสร้างซ็อกเก็ตแล้วจะไม่มีผลใด ๆ ดังนั้นสิ่งนี้จะไม่ช่วยกับเซิร์ฟเวอร์ที่เปิดซ็อกเก็ตไว้นานเกินไป (คุณจะต้องหมุน setTimeout ของคุณเอง) timeout : A number specifying the socket timeout in milliseconds. This will set the timeout before the socket is connected.
UpTheCreek

นี่คือสิ่งที่ฉันกำลังมองหาในการพยายามเชื่อมต่อแบบหยุด การเชื่อมต่อแบบแขวนอาจเกิดขึ้นได้ดีเมื่อพยายามเข้าถึงพอร์ตบนเซิร์ฟเวอร์ที่ไม่ได้รับฟัง BTW API เปลี่ยนเป็นrequest.destroy( abortเลิกใช้แล้ว) นอกจากนี้ยังแตกต่างจากsetTimeout
dturvene

92

อัปเดต 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

ไม่ต้องห่วง. โปรดทราบว่าสิ่งนี้ใช้ได้เฉพาะกับโหนดเวอร์ชันล่าสุดเท่าที่ฉันทราบ ฉันทดสอบกับเวอร์ชันก่อนหน้า (5.0.3-pre) ฉันคิดว่าและมันไม่ได้ทำให้เหตุการณ์ซ็อกเก็ตทำงาน
Rob Evans

1
อีกวิธีหนึ่งในการจัดการปัญหานี้คือการใช้การเรียก setTimeout มาตรฐานที่ไม่เป็นระเบียบ คุณจะต้องเก็บ id setTimeout ไว้ด้วย: var id = setTimeout (... ); เพื่อให้คุณสามารถยกเลิกได้หากคุณได้รับข้อมูลเป็นต้นวิธีที่ดีคือเก็บไว้ในออบเจ็กต์คำขอจากนั้นจึง clearTimeout หากคุณได้รับข้อมูลบางส่วน
Rob Evans

1
คุณหายไป); ในตอนท้ายของความต้องการบน เนื่องจากไม่ใช่อักขระ 6 ตัวฉันจึงแก้ไขให้คุณไม่ได้
JR Smith

วิธีนี้ใช้ได้ผล (ขอบคุณ!) แต่โปรดทราบว่าการตอบกลับจะยังคงมาถึงในที่สุด req.abort()ดูเหมือนจะไม่ใช่ฟังก์ชันมาตรฐานในขณะนี้ ดังนั้นในsocket.onคุณอาจต้องการตั้งค่าตัวแปรเช่นtimeout = trueแล้วจัดการการหมดเวลาอย่างเหมาะสมในตัวจัดการการตอบสนอง
Jonathan Benn

40

ในขณะนี้มีวิธีการดำเนินการโดยตรงกับวัตถุคำขอ:

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

นี่เป็นวิธีทางลัดที่เชื่อมโยงกับเหตุการณ์ของซ็อกเก็ตแล้วสร้างการหมดเวลา

อ้างอิงNode.js v0.8.8 Manual & Documentation


3
request.setTimeout "ตั้งค่าซ็อกเก็ตให้หมดเวลาหลังจากหมดเวลามิลลิวินาทีของการไม่มีการใช้งานบนซ็อกเก็ต" ฉันคิดว่าคำถามนี้เกี่ยวกับการหมดเวลาของคำขอโดยไม่คำนึงถึงกิจกรรม
ostergaard

โปรดทราบว่าเช่นเดียวกับคำตอบด้านล่างที่ใช้ซ็อกเก็ตที่เกี่ยวข้องโดยตรง req.abort () ทำให้เกิดเหตุการณ์ข้อผิดพลาดซึ่งควรจัดการโดย on ('error') เป็นต้น
KLoozen

4
request.setTimeout จะไม่ยกเลิกคำขอเราจำเป็นต้องโทรยกเลิกด้วยตนเองในการเรียกกลับการหมดเวลา
Udhaya

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เหตุการณ์
คิริลล์

9

มีวิธีที่ง่ายกว่านี้

แทนที่จะใช้ 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();

หากคุณใส่ทั้ง 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!

หวังว่าจะช่วยได้


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


การเพิ่มการผูก (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 ("พร้อม") บนซ็อกเก็ต ... ฉันไม่รู้
Claudio

@Claudio คุณสามารถอัปเดตรหัสของคุณเพื่อแสดงคำขอหลายรายการได้หรือไม่?
onteria_

1
แน่นอน ... มันยาวไปหน่อยและฉันใช้ paste2.org ถ้านี่ไม่ใช่ปัญหา: paste2.org/p/1448487
Claudio

@Claudio อืมโอเคการตั้งค่าสภาพแวดล้อมการทดสอบและการเขียนโค้ดทดสอบกำลังจะเกิดขึ้นดังนั้นคำตอบของฉันอาจมาในวันพรุ่งนี้ (เวลาแปซิฟิก) เป็น FYI
onteria_

@Claudio ดูรหัสของคุณดูเหมือนจะไม่ตรงกับข้อผิดพลาดของคุณ มีการบอกว่า setTimeout ถูกเรียกด้วยค่าที่ไม่ได้กำหนด แต่วิธีที่คุณเรียกมันคือผ่านเวอร์ชันสากลดังนั้นจึงไม่มีทางที่จะไม่สามารถกำหนดได้ทำให้ฉันค่อนข้างสับสน
onteria_
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.