Bagaimana cara saya debug kesalahan ECONNRESET di Node.js?

288

Saya menjalankan aplikasi Express.js menggunakan Socket.io untuk webapp obrolan dan saya mendapatkan kesalahan berikut secara acak sekitar 5 kali selama 24 jam. Proses simpul dibungkus selamanya dan segera restart sendiri.

Masalahnya adalah me-restart Express menendang pengguna saya keluar dari kamar mereka dan tidak ada yang menginginkannya.

Server web diproksi oleh HAProxy. Tidak ada masalah stabilitas soket, hanya menggunakan soket web dan soket flash. Saya tidak bisa mereproduksi ini dengan sengaja.

Ini adalah kesalahan dengan 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)

Menambahkan kedua penangan kesalahan klien socket.io dan penangan pengecualian yang tidak tertangkap. Tampaknya yang ini menangkap kesalahan:

    process.on('uncaughtException', function (err) {
      console.error(err.stack);
      console.log("Node NOT Exiting...");
    });

Jadi saya menduga itu bukan masalah Socket.io tetapi permintaan HTTP ke server lain yang saya lakukan atau koneksi MySQL / Redis. Masalahnya adalah bahwa tumpukan kesalahan tidak membantu saya mengidentifikasi masalah kode saya. Berikut ini adalah output log:

    Error: read ECONNRESET
        at errnoException (net.js:900:11)
        at TCP.onread (net.js:555:19)

Bagaimana saya tahu apa yang menyebabkan ini? Bagaimana cara saya mendapatkan lebih banyak dari kesalahan?

Ok, tidak terlalu verbose tapi inilah stacktrace dengan 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] ] } }

Di sini saya melayani file kebijakan soket 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)

Mungkinkah ini penyebabnya?

Samson
sumber
3
@ GottZ mungkin ini bisa membantu (berbicara dengan seseorang yang bekerja di simpul js) gist.github.com/samsonradu/1b0c6feb438f5a53e30e . Saya akan menggunakan socket.error handler hari ini dan memberi tahu Anda.
Samson
1
@ Gottz the socket.error menangani tidak membantu, tetapi memproses.on ('uncaughtException') menangkap kesalahan. Inilah konsol.log kesalahan: {[Kesalahan: baca ECONNRESET] kode: 'ECONNRESET', errno: 'ECONNRESET', syscall: 'read'}
Samson
1
ECONNRESET dapat berasal dari masalah jaringan. Seperti yang Anda ketahui, tidak mungkin untuk menangkap semua pengecualian saat pengujian. Beberapa akan muncul di server produksi Anda. Anda harus membuat server Anda kuat. Anda dapat menangani penghapusan sesi dengan menggunakan Redis sebagai penyimpanan. Itu membuat sesi Anda tetap ada bahkan setelah server simpul Anda turun.
user568109
1
Mengapa itu terkait dengan penghapusan sesi? Mereka tetap ditangani oleh Redis.
Samson
3
Anda memiliki setidaknya satu soket TCP yang mendengarkan yang tidak memiliki set handler. Jadi sekarang saatnya untuk memeriksa di mana itu: D
Moss

Jawaban:

253

Anda mungkin sudah menebaknya: ini adalah kesalahan koneksi.

"ECONNRESET" berarti sisi lain dari percakapan TCP tiba-tiba menutup ujung koneksi. Ini kemungkinan besar disebabkan oleh satu atau lebih kesalahan protokol aplikasi. Anda bisa melihat log server API untuk melihat apakah ada keluhan tentang sesuatu.

Tetapi karena Anda juga mencari cara untuk memeriksa kesalahan dan berpotensi men-debug masalah, Anda harus melihat " Bagaimana cara men-debug kesalahan menutup sambungan soket di NodeJS? " Yang diposting di stackoverflow sehubungan dengan pertanyaan yang sama.

Solusi cepat dan kotor untuk pengembangan :

Gunakan longjohn , Anda mendapatkan jejak tumpukan panjang yang akan berisi operasi async.

Solusi bersih dan benar : Secara teknis, dalam simpul, setiap kali Anda memancarkan suatu 'error'peristiwa dan tidak ada yang mendengarkannya, itu akan melempar . Agar tidak melempar, letakkan pendengar di atasnya dan tangani sendiri. Dengan begitu Anda dapat mencatat kesalahan dengan informasi lebih lanjut.

Untuk memiliki satu pendengar untuk sekelompok panggilan Anda dapat menggunakan domain dan juga menangkap kesalahan lain saat runtime. Pastikan setiap operasi async terkait dengan http (Server / Client) berada dalam konteks domain yang berbeda dibandingkan dengan bagian lain dari kode, domain akan secara otomatis mendengarkan erroracara dan akan menyebarkannya ke penangannya sendiri. Jadi, Anda hanya mendengarkan penangan itu dan mendapatkan data kesalahan. Anda juga mendapatkan informasi lebih lanjut secara gratis.

EDIT (2013-07-22)

Seperti yang saya tulis di atas:

"ECONNRESET" berarti sisi lain dari percakapan TCP tiba-tiba menutup ujung koneksi. Ini kemungkinan besar disebabkan oleh satu atau lebih kesalahan protokol aplikasi. Anda bisa melihat log server API untuk melihat apakah ada keluhan tentang sesuatu.

Apa yang bisa juga terjadi: pada waktu yang acak, pihak lain kelebihan beban dan hanya membunuh koneksi sebagai hasilnya. Jika itu masalahnya, tergantung pada apa yang Anda hubungkan dengan tepat ...

Tapi satu hal yang pasti: Anda memang memiliki kesalahan baca pada koneksi TCP Anda yang menyebabkan pengecualian. Anda dapat melihatnya dengan melihat kode kesalahan yang Anda poskan di edit, yang menegaskan hal itu.

e-sushi
sumber
Itu tidak harus berarti 'tiba-tiba ditutup'. Biasanya hasil dari menulis ke koneksi yang sudah ditutup rekan secara normal. Itu akan menyebabkannya mengeluarkan RST.
Marquis of Lorne
1
@ EJP Ada alasan bagus mengapa saya menulis “dengan tiba-tiba”. Kesalahan (bukan peringatan) menyatakan koneksi diatur ulang oleh rekan. Koneksi yang ada ditutup secara paksa oleh peer jarak jauh. Penutupan paksa tiba-tiba sejak tidak terduga! (Ini biasanya terjadi jika aplikasi rekan pada mesin jarak jauh tiba-tiba dihentikan, mesin di-boot ulang, atau aplikasi rekan menggunakan "hard close" pada soket jarak jauh. Kesalahan ini juga dapat terjadi jika koneksi terputus karena aktivitas "tetap hidup" mendeteksi kegagalan ketika satu atau lebih operasi sedang berlangsung ... operasi ini dan operasi selanjutnya akan gagal.)
e-sushi
2
Saya mendapatkan kesalahan ini ketika saya mengirim sekitar 100 panggilan API secara bersamaan dari browser (Chrome) untuk pengujian. Saya membayangkan Chrome kemudian menjadi kelebihan beban dan mematikan beberapa koneksi ... @Samson - apa yang salah dengan memproses setiap permintaan dalam domainnya sendiri dan menangkap kesalahan domain tanpa me-restart server?
supershnee
2
@supershnee Anda hampir selalu harus me-restart server Anda setelah pengecualian yang tidak tertangkap karena data, aplikasi, dan node.js Anda sendiri dalam keadaan tidak dikenal. Melanjutkan setelah pengecualian membuat data Anda berisiko. Jika Anda ingin mencari tahu lebih lanjut, periksa dokumen Node tentang proses atau dokumen Node pada domain .
c1moore
39

Server tcp sederhana yang saya miliki untuk melayani file kebijakan flash menyebabkan ini. Sekarang saya dapat menangkap kesalahan menggunakan 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)
Samson
sumber
2
Apakah ada yang salah dengan kodenya? Haruskah saya memeriksa apakah soket dapat ditulis sebelum menulis?
Samson
Doh, tidak melihat bahwa Anda sudah menemukan solusinya sebelum saya memposting hal yang hampir sama :) Mengenai pertanyaan Anda, meskipun Anda memeriksa bahwa soket dapat ditulisi, mungkin tidak ketika Anda menulis ke mikrodetik nanti dan masih akan melempar kesalahan, jadi ini adalah "jalan" untuk memastikan.
Joachim Isaksson
ok, dan apakah ada jalan keluar yang aman jika ini? seperti socket.close () di dalam penangan kesalahan? karena saya pikir beban CPU saya meningkat setelah kesalahan ini (tidak yakin)
Samson
2
Saya selalu memanggil socket.destroy()penangan kesalahan untuk memastikan. Sayangnya saya tidak dapat menemukan dokumentasi apakah itu diperlukan, tetapi tidak memunculkan kesalahan untuk melakukannya.
Joachim Isaksson
socket.destroy () menyelamatkan hari saya, apa pun itu berfungsi !! Terima kasih!
Firas Abd Alrahman
27

Saya memiliki masalah serupa di mana aplikasi mulai error setelah peningkatan Node. Saya percaya ini dapat ditelusuri kembali ke rilis Node v0.9.10 item ini:

  • bersih: jangan tekan ECONNRESET (Ben Noordhuis)

Versi sebelumnya tidak akan kesalahan pada gangguan dari klien. Pemutusan koneksi dari klien melempar kesalahan ECONNRESET di Node. Saya percaya ini dimaksudkan fungsionalitas untuk Node, jadi perbaikannya (setidaknya untuk saya) adalah untuk menangani kesalahan, yang saya yakin Anda lakukan dalam pengecualian tanpa pamrih. Meskipun saya menanganinya di net.socket handler.

Anda dapat menunjukkan ini:

Buat server soket sederhana dan dapatkan Node v0.9.9 dan v0.9.10.

require('net')
    .createServer( function(socket) 
    {
           // no nothing
    })
    .listen(21, function()
     {
           console.log('Socket ON')
    })

Mulai menggunakan v0.9.9 dan kemudian coba FTP ke server ini. Saya menggunakan FTP dan port 21 hanya karena saya menggunakan Windows dan memiliki klien FTP, tetapi tidak ada klien telnet yang praktis.

Kemudian dari sisi klien, putuskan koneksi. (Saya hanya melakukan Ctrl-C)

Anda seharusnya melihat NO ERROR saat menggunakan Node v0.9.9, dan ERROR saat menggunakan Node v.0.9.10 dan yang lebih tinggi.

Dalam produksi, saya menggunakan v.0.10. sesuatu dan itu masih memberikan kesalahan. Sekali lagi, saya pikir ini dimaksudkan dan solusinya adalah untuk menangani kesalahan dalam kode Anda.

John Williams
sumber
3
Terima kasih, saya memakukannya sendiri! Sangat penting untuk tidak membiarkan kesalahan menyebar ke uncaughtException karena membuat seluruh aplikasi tidak stabil. Misalnya setelah menangkap sekitar 10 kesalahan ECONNRESET, server terkadang menjadi tidak responsif (hanya membeku dan tidak menangani koneksi apa pun)
Samson
Juga tahu tentang perubahan versi node yang tidak menekan kesalahan lagi, tetapi melihat begitu banyak masalah muncul dan diselesaikan setiap versi saya lebih suka mencari yang terbaru. Saya menggunakan V0.10.13 sekarang btw
Samson
16

Punya masalah yang sama hari ini. Setelah beberapa penelitian saya menemukan --abort-on-uncaught-exceptionopsi node.js yang sangat berguna . Tidak hanya menyediakan jauh lebih banyak verbose dan jejak stack kesalahan berguna, tetapi juga menyimpan file inti pada aplikasi crash memungkinkan debug lebih lanjut.

Suzana_K
sumber
4
aneh bahwa jawaban baru untuk pertanyaan lama ini akan muncul saat saya mencari - tapi ini bagus, terima kasih
Semicolon
13

Saya menghadapi masalah yang sama tetapi saya mengatasinya dengan menempatkan:

server.timeout = 0;

sebelumnya server.listen. serveradalah server HTTP di sini. Batas waktu default adalah 2 menit sesuai dokumentasi API .

Ashish Kaila
sumber
5
Ini bukan solusi melainkan perbaikan cepat yang akan merusak segalanya tanpa membuat kesalahan.
Nishant Ghodke
9

Kasus lain yang mungkin (tetapi jarang) dapat terjadi jika Anda memiliki komunikasi server ke server dan telah menetapkan server.maxConnectionsnilai yang sangat rendah.

Dalam core lib net.js ia akan memanggil clientHandle.close()yang juga akan menyebabkan kesalahan ECONNRESET:

if (self.maxConnections && self._connections >= self.maxConnections) {
  clientHandle.close(); // causes ECONNRESET on the other end
  return;
}
happy_marmoset
sumber
Panggilan bagus, tetapi maxConnectionsnilai standarnya adalah Infinity. Ini hanya akan menjadi kasus (seperti yang Anda katakan) jika Anda telah secara eksplisit menimpa nilai itu.
Gajus
7

Ya, penyajian Anda file kebijakan pasti dapat menyebabkan kerusakan.

Untuk mengulang, tambahkan saja penundaan ke kode Anda:

net.createServer( function(socket) 
{
    for (i=0; i<1000000000; i++) ;
    socket.write("<?xml version=\"1.0\"?>\n");

... dan gunakan telnetuntuk terhubung ke port. Jika Anda memutuskan koneksi telnet sebelum penundaan berakhir, Anda akan mendapatkan crash (pengecualian tanpa tertangkap) ketika socket.write melempar kesalahan.

Untuk menghindari kerusakan di sini, cukup tambahkan penangan kesalahan sebelum membaca / menulis soket:

net.createServer(function(socket)
{
    for(i=0; i<1000000000; i++);
    socket.on('error', function() { console.log("error"); });
    socket.write("<?xml version=\"1.0\"?>\n");
}

Ketika Anda mencoba putuskan di atas, Anda hanya akan mendapatkan pesan log daripada crash.

Dan setelah selesai, ingatlah untuk menghapus penundaan.

Joachim Isaksson
sumber
6

Saya juga mendapatkan kesalahan ECONNRESET selama pengembangan saya, cara saya menyelesaikannya adalah dengan tidak menggunakan nodemon untuk memulai server saya, cukup gunakan "node server.js"untuk memulai server saya perbaiki masalah saya.

Aneh, tapi itu berhasil untuk saya, sekarang saya tidak pernah melihat kesalahan ECONNRESET lagi.

Andrew Lam
sumber
4

Saya juga memiliki Kesalahan ini dan dapat menyelesaikannya setelah berhari-hari melakukan debugging dan analisis:

solusi saya

Bagi saya VirtualBox (untuk Docker) adalah Masalahnya. Saya memiliki Port Forwarding yang dikonfigurasi pada VM saya dan kesalahan hanya terjadi pada port yang diteruskan.

kesimpulan umum

Pengamatan berikut dapat menghemat hari kerja yang harus saya investasikan:

  • Bagi saya masalahnya hanya terjadi pada koneksi dari localhost ke localhost pada satu port. -> periksa mengubah konstanta ini memecahkan masalah.
  • Bagi saya masalahnya hanya terjadi pada mesin saya -> biarkan orang lain mencobanya.
  • Bagi saya masalahnya hanya terjadi setelah beberapa saat dan tidak dapat direproduksi dengan andal
  • Masalah saya tidak dapat diperiksa dengan alat node atau express (debug-) apa pun. -> jangan buang waktu untuk ini

-> mencari tahu apakah ada sesuatu yang main-main dengan jaringan Anda (-pengaturan), seperti VM, Firewall dll, ini mungkin penyebab masalahnya.

Waog
sumber
2

Saya memecahkan masalah hanya dengan menghubungkan ke jaringan yang berbeda . Itu adalah salah satu masalah yang mungkin terjadi.

Seperti yang dibahas di atas, ECONNRESET berarti bahwa percakapan TCP tiba-tiba menutup ujung koneksi.

Koneksi internet Anda mungkin menghalangi Anda untuk terhubung ke beberapa server. Dalam kasus saya, saya mencoba untuk terhubung ke mLab (layanan database cloud yang menampung database MongoDB). Dan ISP saya memblokirnya.

Yousef
sumber
Yang ini bekerja untuk saya, kode saya yang berfungsi dengan baik beberapa jam yang lalu tiba-tiba berhenti bekerja, ternyata, perubahan jaringan menyebabkan masalah
Aklank Jain
2

Saya telah mengatasi masalah ini dengan:

  • Matikan koneksi wifi / ethernet saya dan nyalakan.
  • Saya mengetik: npm updatedi terminal untuk memperbarui npm.
  • Saya mencoba keluar dari sesi dan masuk lagi

Setelah itu saya mencoba perintah npm yang sama dan hal yang baik berhasil. Saya tidak yakin sesederhana itu.

Saya menggunakan CENTOS 7

muhammad tayyab
sumber
0

Saya memiliki masalah yang sama dan tampaknya versi Node.js adalah masalahnya.

Saya menginstal versi Node.js sebelumnya (10.14.2) dan semuanya baik-baik saja menggunakan nvm (memungkinkan Anda untuk menginstal beberapa versi Node.js dan dengan cepat beralih dari satu versi ke versi lain).

Ini bukan solusi "bersih", tetapi dapat melayani Anda untuk sementara waktu.

Sylvain
sumber
0

Saya baru saja menemukan ini, setidaknya dalam kasus penggunaan saya.

Saya mendapatkan ECONNRESET. Ternyata cara klien saya diatur, itu memukul server dengan panggilan API satu ton kali dengan sangat cepat - dan hanya perlu mencapai titik akhir sekali.

Ketika saya memperbaikinya, kesalahan sudah hilang.

VikR
sumber
-2

Coba tambahkan opsi ini ke socket.io:

const options = { transports: ['websocket'], pingTimeout: 3000, pingInterval: 5000 };

Saya harap ini akan membantu Anda!

sol404
sumber