Ik draai een Express js applicatie met socket.io voor een chat webapp en ik krijg de volgende foutmelding willekeurig ongeveer 5 keer gedurende 24h. Het node proces is voor altijd gewikkeld en het herstart zichzelf onmiddellijk opnieuw.
Het probleem is dat het herstarten van express mijn gebruikers uit hun kamers schopt en niemand wil dat.
De webserver wordt proxied door HAProxy. Er zijn geen socket stabiliteitsproblemen, ik gebruik alleen websockets en flashsockets transporten. Ik kan dit niet met opzet reproduceren.
Dit is de fout met node 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)
Toegevoegd zowel socket.io client error handler en de uncaught exception handler. Het lijkt erop dat deze de fout vangt:
process.on('uncaughtException', function (err) {
console.error(err.stack);
console.log("Node NOT Exiting...");
});
Dus ik vermoed dat het geen socket.io probleem is maar een http verzoek naar een andere server of een mysql/redis connectie. Probleem is dat de error stack me niet'helpt mijn code probleem te identificeren. Hier is de log output:
Error: read ECONNRESET
at errnoException (net.js:900:11)
at TCP.onread (net.js:555:19)
Hoe weet ik wat dit veroorzaakt? Hoe krijg ik meer uit de foutmelding?
Ok, niet erg verbose maar hier is de stacktrace met "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] ] } }
Hier dien ik het flash socket beleid bestand:
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)
Kan dit de oorzaak zijn?
Je had het misschien al geraden: het is een verbindingsfout.
"ECONNRESET" betekent dat de andere kant van de TCP conversatie abrupt zijn einde van de verbinding heeft gesloten. Dit is waarschijnlijk te wijten aan een of meer applicatie protocol fouten. Je zou naar de API server logs kunnen kijken om te zien of die ergens over klaagt.
Maar omdat je ook op zoek bent naar een manier om de fout te controleren en mogelijk het probleem te debuggen, zou je een kijkje moeten nemen op "How to debug a socket hang up error in NodeJS?" die werd gepost op stackoverflow in verband met een gelijksoortige vraag.
Snelle en vuile oplossing voor ontwikkeling:
Gebruik longjohn, je krijgt lange stack traces die de async operaties zullen bevatten.
Schone en correcte oplossing: Technisch gezien, in node, telkens wanneer je een
'error'
event emiteert en niemand luistert ernaar, zal het gooien. Om het niet te laten gooien, zet er een luisteraar op en handel het zelf af. Op die manier kun je de fout met meer informatie loggen.
Om één listener te hebben voor een groep aanroepen kun je domeinen gebruiken en ook andere fouten op runtime afvangen. Zorg ervoor dat elke async operatie gerelateerd aan http(Server/Client) in een andere domain context staat in vergelijking met de andere delen van de code, het domein zal automatisch luisteren naar de
error
events en zal deze propageren naar zijn eigen handler. Dus je luistert alleen naar die handler en krijgt de fout gegevens. Je krijgt ook gratis meer informatie
EDIT (2013-07-22)
Zoals ik hierboven al schreef:
"ECONNRESET" betekent dat de andere kant van het TCP gesprek abrupt zijn einde van de verbinding heeft gesloten. Dit is zeer waarschijnlijk te wijten aan een of meer applicatie protocol fouten. Je zou naar de API server logs kunnen kijken om te zien of die ergens over klaagt.
Wat ook het geval zou kunnen zijn: op willekeurige momenten is de andere kant overbelast en verbreekt gewoon de verbinding als gevolg daarvan. Of dat's het geval is, hangt af van waar je'precies verbinding mee maakt...
Maar één ding's zeker: je hebt inderdaad een leesfout op je TCP-verbinding die de uitzondering veroorzaakt. Je kunt dat zien door te kijken naar de foutcode die je in je bewerking hebt gepost, die het bevestigt.
Een eenvoudige tcp server die ik had voor het serveren van het flash beleid bestand veroorzaakte dit. Ik kan de fout nu opvangen met een handler:
# 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)
Ik had een soortgelijk probleem waarbij apps begonnen uit te fouten na een upgrade van Node. Ik geloof dat dit kan worden herleid tot Node release v0.9.10 dit item:
Vorige versies konden geen foutmelding geven bij onderbrekingen van de client. Een onderbreking in de verbinding vanaf de client geeft de fout ECONNRESET in Node. Ik geloof dat dit de bedoelde functionaliteit is voor Node, dus de oplossing (in ieder geval voor mij) was om de fout af te handelen, wat je volgens mij hebt gedaan in unCaught exceptions. Hoewel ik het afhandel in de net.socket handler.
Je kunt dit demonstreren:
Maak een eenvoudige socket server en haal Node v0.9.9 en v0.9.10.
require('net')
.createServer( function(socket)
{
// no nothing
})
.listen(21, function()
{
console.log('Socket ON')
})
Start het op met v0.9.9 en probeer dan om FTP naar deze server te doen. Ik'gebruik FTP en poort 21 alleen omdat ik op Windows zit en een FTP client heb, maar geen telnet client bij de hand.
Dan vanaf de client kant, verbreek gewoon de verbinding. (Ik'doe gewoon Ctrl-C)
Je zou GEEN ERROR moeten zien als je Node v0.9.9 gebruikt, en ERROR als je Node v.0.9.10 en hoger gebruikt.
In productie, gebruik ik v.0.10. iets en het geeft nog steeds de fout. Nogmaals, ik denk dat dit de bedoeling is en de oplossing is om de fout in uw code te behandelen.