2014-06-20 13 views
10

sto usando Node.js - asincrono modulo & richiesta di strisciare 100 + milioni di siti web e continuo a sbattere contro gli errori ESOCKETTIMEDOUT & ETIMEDOUT dopo pochi minuti.Node.js richiesta GET ETIMEDOUT & ESOCKETTIMEDOUT

Funziona di nuovo dopo il riavvio dello script. Non sembra essere un problema di limite di connessione perché posso ancora risolvere resol4, resolveNs, resolveMx e anche curl senza ritardi.

Vedi qualche problema con il codice? o qualche consiglio? Vorrei aumentare la concorrenza async.queue() ad almeno un 1000. Grazie.

var request = require('request'), 
    async = require('async'), 
    mysql = require('mysql'), 
    dns = require('dns'), 
    url = require('url'), 
    cheerio = require('cheerio'), 
    iconv = require('iconv-lite'), 
    charset = require('charset'), 
    config = require('./spy.config'), 
    pool = mysql.createPool(config.db); 

iconv.skipDecodeWarning = true; 

var queue = async.queue(function (task, cb) { 
    dns.resolve4('www.' + task.domain, function (err, addresses) { 
     if (err) { 
      // 
      // Do something 
      // 
      setImmediate(function() { 
       cb() 
      }); 
     } else { 
      request({ 
       url: 'http://www.' + task.domain, 
       method: 'GET', 
       encoding:  'binary', 
       followRedirect: true, 
       pool:   false, 
       pool:   { maxSockets: 1000 }, 
       timeout:  15000 // 15 sec 
      }, function (error, response, body) { 

       //console.info(task); 

       if (!error) { 
        // If ok, do something 

       } else { 
        // If not ok, do these 

        console.log(error); 

        // It keeps erroring here after few minutes, resolve4, resolveNs, resolveMx still work here. 

        // { [Error: ETIMEDOUT] code: 'ETIMEDOUT' } 
        // { [Error: ESOCKETTIMEDOUT] code: 'ESOCKETTIMEDOUT' } 

        var ns = [], 
         ip = [], 
         mx = []; 
        async.parallel([ 
         function (callback) { 
          // Resolves the domain's name server records 
          dns.resolveNs(task.domain, function (err, addresses) { 
           if (!err) { 
            ns = addresses; 
           } 
           callback(); 
          }); 
         }, function (callback) { 
          // Resolves the domain's IPV4 addresses 
          dns.resolve4(task.domain, function (err, addresses) { 
           if (!err) { 
            ip = addresses; 
           } 
           callback(); 
          }); 
         }, function (callback) { 
          // Resolves the domain's MX records 
          dns.resolveMx(task.domain, function (err, addresses) { 
           if (!err) { 
            addresses.forEach(function (a) { 
             mx.push(a.exchange); 
            }); 
           } 
           callback(); 
          }); 
         } 
        ], function (err) { 
         if (err) return next(err); 

         // do something 
        }); 

       } 
       setImmediate(function() { 
        cb() 
       }); 
      }); 
     } 
    }); 
}, 200); 

// When the queue is emptied we want to check if we're done 
queue.drain = function() { 
    setImmediate(function() { 
     checkDone() 
    }); 
}; 
function consoleLog(msg) { 
    //console.info(msg); 
} 
function checkDone() { 
    if (queue.length() == 0) { 
     setImmediate(function() { 
      crawlQueue() 
     }); 
    } else { 
     console.log("checkDone() not zero"); 
    } 
} 

function query(sql) { 
    pool.getConnection(function (err, connection) { 
     if (!err) { 
      //console.log(sql); 
      connection.query(sql, function (err, results) { 
       connection.release(); 
      }); 
     } 
    }); 
} 

function crawlQueue() { 
    pool.getConnection(function (err, connection) { 
     if (!err) { 
      var sql = "SELECT * FROM domain last_update < (UNIX_TIMESTAMP() - 2592000) LIMIT 500"; 
      connection.query(sql, function (err, results) { 
       if (!err) { 
        if (results.length) { 
         for (var i = 0, len = results.length; i < len; ++i) { 
          queue.push({"id": results[i]['id'], "domain": results[i]['domain'] }); 
         } 
        } else { 
         process.exit(); 
        } 
        connection.release(); 
       } else { 
        connection.release(); 
        setImmediate(function() { 
         crawlQueue() 
        }); 
       } 
      }); 
     } else { 
      setImmediate(function() { 
       crawlQueue() 
      }); 
     } 
    }); 
} 
setImmediate(function() { 
    crawlQueue() 
}); 

E i limiti di sistema sono piuttosto elevati.

Limit      Soft Limit   Hard Limit   Units 
    Max cpu time    unlimited   unlimited   seconds 
    Max file size    unlimited   unlimited   bytes 
    Max data size    unlimited   unlimited   bytes 
    Max stack size   8388608    unlimited   bytes 
    Max core file size  0     unlimited   bytes 
    Max resident set   unlimited   unlimited   bytes 
    Max processes    257645    257645    processes 
    Max open files   500000    500000    files 
    Max locked memory   65536    65536    bytes 
    Max address space   unlimited   unlimited   bytes 
    Max file locks   unlimited   unlimited   locks 
    Max pending signals  257645    257645    signals 
    Max msgqueue size   819200    819200    bytes 
    Max nice priority   0     0 
    Max realtime priority  0     0 
    Max realtime timeout  unlimited   unlimited   us 

sysctl

net.ipv4.ip_local_port_range = 10000 61000 
+0

perché il pool (su richiesta) è impostato due volte? – dandavis

+0

Serve per disabilitare il pool. Ricevo ancora gli errori, con o senza pool e maxSockets. –

+7

sei riuscito a trovare la causa? – Cmag

risposta

7

Per impostazione predefinita, nodo ha 4 workers to resolve DNS queries. Se la query DNS impiega molto tempo, le richieste verranno bloccate nella fase DNS e il sintomo è esattamente ESOCKETTIMEDOUT o ETIMEDOUT.

provare ad aumentare il vostro uv dimensioni del pool di thread:

export UV_THREADPOOL_SIZE=128 
node ... 

o in index.js (o dovunque il tuo punto di ingresso è):

#!/usr/bin/env node 
process.env.UV_THREADPOOL_SIZE = 128; 

function main() { 
    ... 
} 

I reproduced this locally rallentando le risposte dal server DNS utilizzando tc.

+0

ho lo stesso problema, ma questa soluzione alternativa non funziona per me. Qualche idea? –

+0

Non funziona neanche per me. – 3zzy

2

Ho avuto lo stesso problema. Viene risolto utilizzando "agent: false" nell'opzione di richiesta dopo aver letto this discussion.

30/10/2017 Quanto sopra non sembra completo per risolvere il problema. La soluzione finale che abbiamo trovato è usare l'opzione keepAlive in un agente. Ad esempio:

var pool = new https.Agent({ keepAlive: true }); 

function getJsonOptions(_url) { 
    return { 
     url: _url, 
     method: 'GET', 
     agent: pool, 
     json: true 
    }; 
} 

Il pool di default del nodo sembra essere predefinito su keepAlive = false che causa la creazione di una nuova connessione su ogni richiesta. Quando vengono create troppe connessioni in un breve periodo di tempo, l'errore precedente verrebbe fuoriuscito. La mia ipotesi è che uno o più router lungo il percorso verso il servizio blocchino la richiesta di connessione, probabilmente in sospetto di attacco Deny Of Service. In ogni caso, l'esempio di codice sopra completamente risolto il nostro problema.

Problemi correlati