I m teče Express js aplikacijo z socket.io za klepet in se mi približno petkrat naključno pojavi naslednja napaka 24h. Proces node se zavije v večnost in se znova zažene se takoj znova vzpostavi.
Težava je v tem, da ponovni zagon ekspresno izstreli moje uporabnike iz njihovih sob in tega si nihče ne želi.
Spletni strežnik je posredovan s posrednikom HAProxy. Ni težav s stabilnostjo vtičnic, uporabljam samo prenos spletnih vtičnic in vtičnic flashsockets. Tega ne morem namerno reproducirati.
To je napaka z vozliščem 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
OPRAVLJENO (2013-07-22)
Dodan je bil ročnik za napake odjemalca socket.io in ročnik za nezajete izjeme. Zdi se, da ta ujame napako:
process.on('uncaughtException', function (err) {
console.error(err.stack);
console.log("Node NOT Exiting...");
});
Zato sumim, da ne gre za težavo socket.io, temveč za zahtevo http na drug strežnik, ki jo izvajam, ali povezavo mysql/redis. Težava je v tem, da mi niz napak ne pomaga ugotoviti težave s kodo. Tukaj je izpis dnevnika:
Error: read ECONNRESET
at errnoException (net.js:900:11)
at TCP.onread (net.js:555:19)
Kako naj vem, kaj to povzroča? Kako lahko izvem več o napaki?
Okej, ne preveč razglabljam, ampak tukaj je stacktrace z "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] ] } }
Tukaj podajam datoteko s politiko vtičnic flash:
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)
Ali je to lahko vzrok?
Morda ste že uganili: gre za napako povezave.
"ECONNRESET" pomeni, da je druga stran pogovora TCP nenadoma zaprla svoj konec povezave. To je najverjetneje posledica ene ali več napak aplikacijskega protokola. Lahko si ogledate dnevnike strežnika API in preverite, ali se pritožuje zaradi česa drugega.
Ker pa iščete tudi način za preverjanje napake in morebitno odpravljanje težav, si oglejte "[How to debug a socket hang up error in NodeJS?] (https://stackoverflow.com/a/11542134/2432317)", ki je bil objavljen na stackoverflow v zvezi s podobnim vprašanjem.
Hitra in umazana rešitev za razvoj:
Uporabite longjohn, dobite dolge sledove, ki bodo vsebovali asinhronizirane operacije.
Čista in pravilna rešitev: Tehnično gledano bo v vozlišču, kadar koli oddaš dogodek
'error'
in ga nihče ne posluša, vrgel. Če želite, da se dogodek ne vrže, nanj namestite poslušalca in ga obravnavajte sami. Tako lahko napako prijavite z več informacijami.
Če želite imeti enega poslušalca za skupino klicev, lahko uporabite domene in ob izvajanju lovite tudi druge napake. Prepričajte se, da je vsaka asinhronizirana operacija, povezana s http(strežnik/odjemalec), v drugem kontekstu domena v primerjavi z drugimi deli kode; domena bo samodejno poslušala dogodke
error
in jih posredovala lastnemu izvajalcu. Tako boste poslušali samo to izvajalno okno in dobili podatke o napaki. [Brezplačno dobite tudi več informacij] (http://nodejs.org/api/all.html#all_additions_to_error_objects).
OPRAVLJENO (2013-07-22)
Kot sem zapisal zgoraj:
"ECONNRESET" pomeni, da je druga stran pogovora TCP nenadoma zaprla svoj konec povezave. To je najverjetneje posledica ene ali več napak aplikacijskega protokola. Lahko si ogledate dnevnike strežnika API, da vidite, ali se pritožuje zaradi česa.
Lahko se zgodi tudi naslednje: druga stran je ob naključnih priložnostih preobremenjena in zaradi tega preprosto prekine povezavo. Če je tako, je odvisno od tega, s čim se povezujete...
Toda nekaj je gotovo: v povezavi TCP imate res napako pri branju, ki povzroči izjemo. To lahko vidite tako, da pogledate kodo napake, ki ste jo objavili v svojem urejanju, ki to potrjuje.
To je povzročal preprost strežnik TCP, ki sem ga imel za strežbo datoteke politike flash. Napako lahko zdaj ujamem z uporabo obdelave:
# 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)
Imel sem podobno težavo, ko so se aplikacije po nadgradnji vozlišča začele pojavljati napake. Verjamem, da je to mogoče izslediti do izdaje Node v0.9.10 te postavke:
Pri prejšnjih različicah ni prišlo do napake ob prekinitvah s strani odjemalca. Prekinitev povezave s strani odjemalca vrže napako ECONNRESET v vozlišču Node. Verjamem, da je to predvidena funkcionalnost za Node, zato je bila rešitev (vsaj zame) v tem, da je bilo treba napako obravnavati, kar ste po mojem mnenju storili v unCaught izjemah. Jaz jo sicer obravnavam v obdelavi net.socket.
To lahko pokažete:
Node v0.9.9 in v0.9.10.
require('net')
.createServer( function(socket)
{
// no nothing
})
.listen(21, function()
{
console.log('Socket ON')
})
Zagonite ga s strežnikom v0.9.9, nato pa poskusite v ta strežnik poslati sporočilo FTP. Uporabljam FTP in vrata 21 samo zato, ker sem v sistemu Windows in imam odjemalca FTP, nimam pa odjemalca telnet.
Nato na strani odjemalca prekinite povezavo. (jaz sem samo Ctrl-C)
Če uporabljate vozlišče Node v0.9.9, ne bi smelo biti napak, če pa uporabljate vozlišče Node v0.9.10 in novejše, bi se morale pojaviti napake.
V produkciji uporabljam Node v.0.10. nekaj in še vedno daje napako. Ponovno menim, da je to predvideno in rešitev je, da napako obravnavate v svoji kodi.