2012-10-12 8 views
6

EDITNode.js + espresso gocce a caso le richieste, con un conseguente Gateway Timeout

Dopo molte Putzing in giro, ho finalmente trovato qualcosa che sembra come potrebbe essere un vantaggio solido:

La biblioteca espresso non riesce ad accettare la richiesta in arrivo quando sta attualmente utilizzando il modulo Nodo + OAuth per eseguire diverse richieste in uscita (ad esempio, su Facebook, Twitter, ecc.). Sono stato in grado di determinarlo inserendo molti log nel mio codice, in cui ho scoperto che il log "begin-request" (descritto di seguito) non veniva attivato quando si trovava nel mezzo di una richiesta in uscita.

Sono stato in grado di dimostrare in modo dimostrabile che quando il modulo Node + OAuth effettua alcune richieste in uscita, le richieste in entrata alla mia API (tramite una finestra del browser) si bloccheranno e non saranno ricevute fino a una di quelle richieste OAuth in uscita ha finito.

Certo, ho già fatto:

require('http').globalAgent.maxSockets = 999; 

Per un suggerimento in IRC, ho aggiunto

console.log(require('http').globalAgent.requests); 

Ma questo sembra sempre di essere === {}, che implica che non ci siano richieste in entrata in sospeso AFAIK.

Così non mi resta che concludere che sia node.js o esprimere è la scelta di, per qualche motivo, bloccare le richieste in entrata a causa delle richieste in uscita, anche se non ci dovrebbero essere molti socket disponibili ...

Qualcuno ha qualche suggerimento su come risolvere questo?


devo un'API creata in node.js utilizzando Express, Mongoose, ecc distribuito su Amazon cloud, che funziona meravigliosamente e veloce il 99% del tempo.

Tranne che, una volta tanto, una richiesta sembra essere in qualche modo abbandonata o altrimenti ignorata. Sto parlando di richieste che di solito completano in millisecondi in modo casuale non rispondono con nessuna immagine chiara perché.

Il sintomo è un semplice "Timeout gateway" quando ci si connette all'endpoint API. Una richiesta identica, fatta dallo stesso cliente con gli stessi parametri, pochi istanti prima o pochi istanti dopo, funzionerà perfettamente.

Naturalmente, il mio primo pensiero è stato "duh, sovraccarico del server!" Così ho trascorso molto tempo ad ottimizzare le mie richieste, monogoDB, ecc. Infine sono arrivato al punto che l'utilizzo della CPU/disco/RAM su tutta la scheda (sia nei server Node.js che nei server Mongo) era molto basso.. Uso Scout e RightScale per tenere traccia dei miei server in tempo reale e registro qualsiasi richiesta o query che richiede più di 100 ms. I miei server di nodi attualmente hanno 5 GB di RAM GRATUITA, 70% di CPU libera (sul 1 ° core), ecc. Quindi sono sicuro al 99,99% che non si tratta di un problema di prestazioni.

Infine, sono rientrato in un tentativo disperato: ho allegato un numero casuale a tutte le richieste fatte dai miei clienti. Quindi, nell'app node.js, eseguo un console.log() quando la richiesta viene prima ricevuta e quando viene completata. Ad esempio, ecco il middleware che uso in espresso:

var configureAPI = function() { 
    return function(req, res, next) { 
     if(req.body.ruid) 
      console.log(req.body.ruid); 

     // more middleware stuff... 
    }; 
} 
server.configure(function(){ 

    server.use(express.bodyParser()); 
    server.use(configureAPI()); 
    server.use(onError); 

    // ... more config stuff 
} 

Quello che ho trovato mi ha scioccato: a quanto pare, il nodo.l'app di js non riceve nemmeno le richieste in questione. Ho una webapp Javascript e stampo il "ruid" inviato con la richiesta alla console. Ogni volta che la richiesta ha esito positivo, c'è un "ruid" corrispondente stampato nella console node.js. Ogni volta che va a finire, non c'è.


Modifica: ulteriori informazioni di debug &.

I miei server di app in realtà hanno iniziato (e continuano) a servire anche PHP (quindi hanno installato Apache, ecc.). Avevo bisogno di http://streamified.me per servire il mio sito web (PHP) e per servire la mia API (node.js) ... quindi ho una riga nel mio file httpd.conf per causare richieste a api.streamified.me (invece di streamified.me) per andare a node.js tramite porta 8888:

RewriteCond %{HTTP_HOST} ^api.streamified.me 
RewriteRule ^(.*) http://localhost:8888$1 [P] 

Così, nello stesso file httpd.conf, ho acceso RewriteLogLevel 5 e poi creato un semplice PHP + codice CURL sul mio localhost per colpire la mia api. streamified.me con un URL casuale (che dovrebbe causare node.js per innescare una semplice risposta "non trovata") fino a quando non ha generato un timeout del gateway. Qui, puoi vedere che è successo - e il log di riscrittura mostra che la richiesta è stata sicuramente ricevuta dal server dell'app e inoltrata alla porta 8888 ... ma non è mai stata ricevuta da node.js (o, almeno, la prima riga di codice nella prima riga del middleware non lo ottiene ...)

enter image description here


sono stato più e più volte il mio codice node.js e sono abbastanza sicuro che non ho il blocco codice, e anche se l'ho fatto, non riesco a immaginare che blocchi il thread abbastanza a lungo da mancare una richiesta senza che essa alzi una bandiera rossa da qualche parte.

Cosa mi manca? C'è qualche ragione per cui il socket in arrivo verrebbe bloccato? Faccio un buon numero di richieste HTTP a API esterne tramite la mia app node.js, ma AFAIK che non dovrebbe bloccare i socket in ingresso.


Naturalmente, ho errori di registrazione in atto. L'ho abilitato a livello di processo ...

process.addListener("uncaughtException", function (err) { 
    // some logging code 
} 

e sul livello Express (il gestore onError sopra). So che le mie funzioni di registrazione degli errori funzionano, perché li ho visti entrambi sparare prima. Ma nessuno di loro riportano nulla intorno al periodo delle richieste sceso, né mi vedo nulla nella console ...


  • Express versione: 3.0.0rc5
  • Node.js versione: 0.8. 12
  • 2 istanze del app node.js in esecuzione su una configurazione standard Amazon cloud (istanze m1.large), dietro 2 di bilanciamento del carico, la connessione a una serie 3x replica di MongoDBs (anche m1.large)
+0

Hai confermato che i bilanciatori di carico stanno ricevendo la richiesta e l'hanno inviata correttamente ai server del nodo? Con quale frequenza stai facendo richieste quando uno fallisce? – Bill

+0

Gli stessi LB/app server servono anche file PHP, che non comportano mai un timeout. Non sono abbastanza sicuro di come confermare che i LB si stanno inoltrando correttamente ai server Node, a parte questo. Non sto mostrando alcun picco nel traffico; i log di apache su Rightscale riportano un costante ~ 10 req/sec. –

+0

Ho trovato un paio di bug elencati che descrivevano problemi simili ma erano tutti riparati da 0.6.6. Potresti provare ad aggiornare alla versione più recente poiché ci sono state un sacco di correzioni/miglioramenti da 0.6. Ti suggerisco anche di installare uno sniffer di rete sui tuoi server delle app per assicurarti che i server stiano effettivamente ricevendo i pacchetti. – Bill

risposta

1

Sembra che tu stia bloccando il thread del Nodo troppo a lungo, causando il timeout delle connessioni in entrata prima di gestirle. Il nodo è a thread singolo, quindi fa solo una cosa alla volta, non può scegliere di bloccare una richiesta in entrata a causa di una richiesta in uscita. Può solo non riuscire ad accettare una richiesta in arrivo perché è occupata a fare qualcos'altro. Devi capire cosa è impegnato a fare.

Se non si effettuano le richieste in uscita, tutto funziona correttamente?In tal caso, è necessario esaminare il codice che effettua tali richieste per assicurarsi che non si stiano aspettando le risposte.

+0

Questo ha senso. Sono sicuro che non sto facendo alcuna attività di "sincronizzazione" e uso Q per attuare le promesse. L'unica cosa che mi viene in mente sono i comandi JSON.parse(), che stanno valutando stringhe di dati grandi (~ 2MB). Questi tipi di operazioni potrebbero bloccare il thread? –

+0

Alcuni dati potrebbero restituire molto più di 2 MB? 2MB non dovrebbe appendere il thread abbastanza a lungo da far cadere le richieste (anche se bloccherà le cose per un po '), ma se di tanto in tanto cerchi di analizzare stringhe molto più grandi, potrebbe essere il colpevole. Potresti provare a sostituire il parse con una chiamata per restituire dati statici per vedere se questo risolve il problema. – Bill

+0

Hm, non posso veramente usare i dati statici, perché l'analisi di JSON sta effettuando successivamente le cose a valle, e usando i dati statici proverei solo 1 scenario ... in ogni caso, ho iniziato a emettere parse() volte, che sono a volte intorno a ~ 100ms (e ci sono spesso diversi back-to-back, anche se separati da promesse). Inoltre, ho appena avuto la mia prima occorrenza di un errore OOM "ERRORE FATALE: CALL_AND_RETRY_2 Assegnazione fallita - elaborazione fuori memoria" ... che mi fa chiedere se potrebbe essere parte del problema ... anche se è la prima volta che Ho mai visto un errore simile ... –

Problemi correlati