Sto eseguendo un'applicazione Express js con socket.io per una chat e ottengo il seguente errore in modo casuale circa 5 volte durante 24h. Il processo node è avvolto per sempre e si riavvia se stesso immediatamente.
Il problema è che il riavvio di express butta fuori i miei utenti dalle loro stanze e nessuno vuole questo.
Il server web è proxy da HAProxy. Non ci sono problemi di stabilità dei socket, sto solo usando i trasporti websockets e flashsockets. Non posso riprodurre questo di proposito.
Questo è l'errore con il nodo v0.10.11:
events.js:72
throw er; // Unhandled 'error' event
^
Error: read ECONNRESET //alternatively it s a 'write'
at errnoException (net.js:900:11)
at TCP.onread (net.js:555:19)
error: Forever detected script exited with code: 8
error: Forever restarting script for 2 time
EDIT (2013-07-22)
Aggiunto sia il gestore degli errori del client socket.io che il gestore delle eccezioni non catturate. Sembra che questo catturi l'errore:
process.on('uncaughtException', function (err) {
console.error(err.stack);
console.log("Node NOT Exiting...");
});
Quindi sospetto che non sia un problema di socket.io ma una richiesta http ad un altro server che faccio o una connessione mysql/redis. Il problema è che lo stack degli errori non mi aiuta a identificare il mio problema di codice. Ecco l'output del log:
Error: read ECONNRESET
at errnoException (net.js:900:11)
at TCP.onread (net.js:555:19)
Come faccio a sapere cosa causa questo? Come posso ottenere di più dall'errore?
Ok, non molto prolisso, ma ecco lo stacktrace con "longjohn":
Exception caught: Error ECONNRESET
{ [Error: read ECONNRESET]
code: 'ECONNRESET',
errno: 'ECONNRESET',
syscall: 'read',
__cached_trace__:
[ { receiver: [Object],
fun: [Function: errnoException],
pos: 22930 },
{ receiver: [Object], fun: [Function: onread], pos: 14545 },
{},
{ receiver: [Object],
fun: [Function: fireErrorCallbacks],
pos: 11672 },
{ receiver: [Object], fun: [Function], pos: 12329 },
{ receiver: [Object], fun: [Function: onread], pos: 14536 } ],
__previous__:
{ [Error]
id: 1061835,
location: 'fireErrorCallbacks (net.js:439)',
__location__: 'process.nextTick',
__previous__: null,
__trace_count__: 1,
__cached_trace__: [ [Object], [Object], [Object] ] } }
Qui servo il file di policy di flash socket:
net = require("net")
net.createServer( (socket) =>
socket.write("<?xml version=\"1.0\"?>\n")
socket.write("<!DOCTYPE cross-domain-policy SYSTEM \"http://www.macromedia.com/xml/dtds/cross-domain-policy.dtd\">\n")
socket.write("<cross-domain-policy>\n")
socket.write("<allow-access-from domain=\"*\" to-ports=\"*\"/>\n")
socket.write("</cross-domain-policy>\n")
socket.end()
).listen(843)
Può essere questa la causa?
Forse avete già indovinato: è un errore di connessione.
"ECONNRESET" significa che l'altro lato della conversazione TCP ha chiuso bruscamente la sua fine della connessione. Questo è molto probabilmente dovuto a uno o più errori di protocollo dell'applicazione. Potresti guardare i log del server API per vedere se si lamenta di qualcosa.
Ma poiché stai anche cercando un modo per controllare l'errore e potenzialmente eseguire il debug del problema, dovresti dare un'occhiata a "Come eseguire il debug di un errore di sospensione della presa in NodeJS?" che è stato pubblicato su stackoverflow in relazione a una domanda simile.
Soluzione rapida e sporca per lo sviluppo:
Usa longjohn, ottieni lunghe tracce di stack che conterranno le operazioni async.
Soluzione pulita e corretta: Tecnicamente, in nodo, ogni volta che emetti un evento
'errore'
e nessuno lo ascolta, esso getterà. Per non farlo lanciare, metti un listener su di esso e gestiscilo tu stesso. In questo modo puoi registrare l'errore con più informazioni.
Per avere un ascoltatore per un gruppo di chiamate potete usare domini e catturare anche altri errori a runtime. Assicurati che ogni operazione asincrona relativa a http(Server/Client) sia in un contesto domain diverso rispetto alle altre parti del codice, il dominio ascolterà automaticamente gli eventi errore
e li propagherà al proprio gestore. Così si ascolta solo quel gestore e si ottengono i dati dell'errore. [Si ottengono anche altre informazioni gratuitamente] (http://nodejs.org/api/all.html#all_additions_to_error_objects)
EDIT (2013-07-22)
Come ho scritto sopra:
"ECONNRESET" significa che l'altro lato della conversazione TCP ha chiuso bruscamente la sua fine della connessione. Questo è molto probabilmente dovuto a uno o più errori di protocollo dell'applicazione. Potresti guardare i log del server API per vedere se si lamenta di qualcosa.
Cosa potrebbe anche essere il caso: in momenti casuali, l'altro lato è sovraccarico e semplicemente uccide la connessione come risultato. Se questo è il caso, dipende da cosa ti stai connettendo esattamente...
Ma una cosa è certa: hai effettivamente un errore di lettura sulla tua connessione TCP che causa l'eccezione. Lo puoi vedere guardando il codice di errore che hai postato nella tua modifica, che lo conferma.
Un semplice server tcp che avevo per servire il file di policy flash stava causando questo. Ora posso catturare l'errore usando un gestore:
# serving the flash policy file
net = require("net")
net.createServer((socket) =>
//just added
socket.on("error", (err) =>
console.log("Caught flash policy server socket error: ")
console.log(err.stack)
)
socket.write("<?xml version=\"1.0\"?>\n")
socket.write("<!DOCTYPE cross-domain-policy SYSTEM \"http://www.macromedia.com/xml/dtds/cross-domain-policy.dtd\">\n")
socket.write("<cross-domain-policy>\n")
socket.write("<allow-access-from domain=\"*\" to-ports=\"*\"/>\n")
socket.write("</cross-domain-policy>\n")
socket.end()
).listen(843)
Ho avuto un problema simile in cui le app hanno iniziato a dare errore dopo un aggiornamento di Node. Credo che questo possa essere ricondotto alla release v0.9.10 di Node questo articolo:
Le versioni precedenti non davano errore sulle interruzioni dal client. Un'interruzione della connessione dal client lancia l'errore ECONNRESET in Node. Credo che questa sia una funzionalità prevista per Node, quindi la correzione (almeno per me) era di gestire l'errore, che credo tu abbia fatto in unCaught exceptions. Anche se lo gestisco nel gestore net.socket.
Puoi dimostrarlo:
Fai un semplice server socket e prendi Node v0.9.9 e v0.9.10.
require('net')
.createServer( function(socket)
{
// no nothing
})
.listen(21, function()
{
console.log('Socket ON')
})
Avviatelo usando la v0.9.9 e poi tentate di inviare un FTP a questo server. Sto usando FTP e la porta 21 solo perché sono su Windows e ho un client FTP, ma nessun client telnet a portata di mano.
Quindi dal lato client, basta interrompere la connessione. (Sto solo facendo Ctrl-C)
Non dovresti vedere nessun errore quando usi Node v0.9.9, ed ERRORE quando usi Node v.0.9.10 e superiori.
In produzione, uso la v.0.10. qualcosa e dà ancora l'errore. Di nuovo, penso che questo sia inteso e la soluzione è gestire l'errore nel tuo codice.