Düğümdeki bir http.request () için zaman aşımı nasıl ayarlanır?


94

Şanssız http.request kullanan bir HTTP istemcisinde bir zaman aşımı ayarlamaya çalışıyorum. Şimdiye kadar yaptığım şey şuydu:

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

Herhangi bir ipucu?


1
Bu yanıtı buldum (işe yarıyor) ancak http.request () stackoverflow.com/questions/6129240/…
Claudio

Yanıtlar:


28

Sadece yukarıdaki cevabı netleştirmek için :

Artık timeoutseçeneği ve ilgili istek olayını kullanmak mümkündür :

// 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
Bunun olandan farklı olup olmadığını merak ediyorumsetTimeout(req.abort.bind(req), 3000);
Alexander Mills

@AlexanderMills, o zaman muhtemelen istek iyi çalıştığında zaman aşımını manuel olarak temizlemek istersiniz.
Sergei Kovalenko

4
Bunun kesinlikle connectzaman aşımı olduğunu ve soket kurulduktan sonra hiçbir etkisi olmadığını unutmayın. Yani bu, soketi çok uzun süre açık tutan bir sunucuya yardımcı olmayacaktır (yine de setTimeout ile kendi sunucunuzu açmanız gerekecektir). timeout : A number specifying the socket timeout in milliseconds. This will set the timeout before the socket is connected.
UpTheCreek

Asılı bir bağlantı girişiminde aradığım şey bu. Dinlemeyen bir sunucudaki bir bağlantı noktasına erişmeye çalışırken, kapalı bağlantılar biraz olabilir. BTW, API olarak değişti request.destroy( abortkullanımdan kaldırıldı.) Ayrıca, bu durumsetTimeout
dturvene'den

92

2019 Güncellemesi

Şimdi bunu daha zarif bir şekilde halletmenin çeşitli yolları var. Lütfen bu ileti dizisindeki diğer yanıtlara bakın. Teknoloji hızlı hareket eder, bu nedenle yanıtlar genellikle oldukça hızlı bir şekilde güncelliğini yitirebilir. Cevabım yine işe yarayacak ama alternatiflere de bakmaya değer.

2012 Cevap

Kodunuzu kullanırken sorun, soket nesnesinde bir şeyler ayarlamaya çalışmadan önce isteğe bir soketin atanmasını beklememiş olmanızdır. Hepsi eşzamansız, yani:

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

İsteğe bir soket nesnesi atandığında 'soket' olayı tetiklenir.


Bu gerçekten mantıklı. Sorun şu ki, şimdi bu sorunu test edemiyorum (zaman geçiyor ...). Bu yüzden cevabı şimdilik sadece yükseltebilirim :) Teşekkür ederim.
Claudio

Telaşa gerek yok. Bunun bildiğim kadarıyla yalnızca düğümün en son sürümünde çalıştığını unutmayın. Sanırım önceki bir sürümde (5.0.3-pre) test ettim ve soket olayını ateşlemedi.
Rob Evans

1
Bunu halletmenin diğer yolu, bataklık standardı setTimeout çağrısı kullanmaktır. SetTimeout kimliğini şu şekilde tutmanız gerekir: var id = setTimeout (...); böylece bir veri vb. alırsanız iptal edebilirsiniz. İyi bir yol, onu istek nesnesinin kendisinde saklamak ve sonra bazı veriler alırsanızTimeout'u temizlemek.
Rob Evans

1
Kaçırıyorsun ); talep sonunda 6 karakter olmadığı için sizin için düzenleyemem.
JR Smith

Bu işe yarıyor (teşekkürler!), Ancak yanıtın eninde sonunda geleceğini unutmayın. req.abort()şu anda standart işlevsellik gibi görünmüyor. Bu nedenle, yanıt işleyicisindeki socket.ongibi bir değişken ayarlamak timeout = trueve ardından zaman aşımını uygun şekilde işlemek isteyebilirsiniz
Jonathan Benn

40

Şu anda bunu doğrudan istek nesnesi üzerinde yapmak için bir yöntem var:

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

Bu, soket olayına bağlanan ve ardından zaman aşımını oluşturan bir kısayol yöntemidir.

Başvuru: Node.js v0.8.8 Kılavuzu ve Belgeler


3
request.setTimeout "soketi, sokette zaman aşımına uğrayan milisaniyelik hareketsizlikten sonra zaman aşımına ayarlar. Bu sorunun, etkinlikten bağımsız olarak isteği zaman aşımına uğratmakla ilgili olduğunu düşünüyorum.
ostergaard

Lütfen, ilgili soketi doğrudan kullanan aşağıdaki yanıtlarda olduğu gibi, req.abort () '
un

4
request.setTimeout isteği iptal etmeyecek, zaman aşımı geri aramasında manuel olarak iptal çağrısı yapmamız gerekiyor.
Udhaya

18

Rob Evans yanıtlayıcısı benim için doğru çalışıyor ancak request.abort () kullandığımda, işlenmeden kalan bir soket kapatma hatası veriyor.

İstek nesnesi için bir hata işleyici eklemem gerekiyordu:

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
myTimeoutfonksiyon nerede ? (düzenleme: dokümanlar şöyle der: Zaman aşımı olay düğümüne bağlanma ile aynı )
Ben Muircroft

ECONNRESETHer iki durumda da olabileceğine dikkat edin : istemci soketi kapatır ve sunucu bağlantıyı kapatır. Müşteri tarafından abort()özel bir abortolay olduğunu arayarak yapılıp yapılmadığını belirlemek için
Kirill

9

Daha basit bir yöntem var.

SetTimeout kullanmak veya doğrudan soket ile çalışmak yerine,
istemci kullanımlarında 'seçenekler' içinde 'zaman aşımı' kullanabiliriz.

Aşağıda hem sunucunun hem de istemcinin kodu 3 kısımdır.

Modül ve seçenekler bölümü:

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

Sunucu bölümü:

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

Müşteri bölümü:

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

Yukarıdaki 3 parçanın tümünü tek bir dosyaya, "a.js" dosyasına koyarsanız ve ardından şunu çalıştırın:

node a.js

daha sonra çıktı:

startServer
Server listening on http://127.0.0.1:3000

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

Umarım yardımcı olur.


2

Benim için - işte bunu yapmanın daha az kafa karıştırıcı bir yolu 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 cevabını burada detaylandırmak, bir http talebinde zaman aşımı koyacağınız yerdir.

// 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 () da iyidir.


1

Referansı aşağıdaki gibi talebe iletmelisiniz

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

Hata isteği olayı tetiklenecek

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


Bağlama (req) eklemek benim için hiçbir şeyi değiştirmedi. Bu durumda bind ne yapar?
SpiRail

-1

Merak ediyorum, net.socketsbunun yerine düz kullanırsanız ne olur ? Test amacıyla bir araya getirdiğim bazı örnek kodlar:

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

Soket zaman aşımını kullanırsam ve birbiri ardına iki istek yayınlarsam (ilkinin bitmesini beklemeden), ikinci istekte yuva tanımsız olur (en azından zaman aşımını ayarlamaya çalıştığım anda) .. belki de olmalı sokette ("hazır") gibi bir şey ... Bilmiyorum.
Claudio

@Claudio Kodunuzu, birden fazla istek yapıldığını gösterecek şekilde güncelleyebilir misiniz?
onteria_

1
Tabii ki ... biraz uzun ve sorun değilse, paste2.org kullandım: paste2.org/p/1448487
Claudio

@Claudio Hmm tamam, bir test ortamı kurmak ve bazı test kodları yazmak biraz zaman alacak, bu yüzden
cevabım

@Claudio aslında kodunuza bir göz atmak, hatanızla eşleşmiyor gibi görünüyor. SetTimeout'un tanımlanmamış bir değere çağrıldığını söylüyor, ancak onu çağırma şekliniz küresel versiyondan geçiyor, bu yüzden tanımsız olmanın yolu yok, kafamı karıştırıyor.
onteria_
Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.