Як налагодити помилку ECONNRESET в Node.js?


288

Я запускаю програму Express.js, використовуючи Socket.io для чату webapp, і я отримую таку помилку випадковим чином приблизно 5 разів протягом 24 годин. Процес вузла завернуто назавжди і він негайно перезапуститься.

Проблема полягає в тому, що перезапуск Express виганяє моїх користувачів із своїх кімнат, і цього ніхто не хоче.

Веб-сервер проксі-сервер HAProxy. Проблем із стабільністю розетки немає, просто використовуючи веб-розетки та транспортні флеш-розетки. Я не можу це відтворити спеціально.

Це помилка з вузлом 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)

Додано як обробник помилок клієнта socket.io, так і обробник винятків, що не використовується. Здається, що ця виявляє помилку:

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

Тому я підозрюю, що це не проблема Socket.io, а запит HTTP на інший сервер, який я роблю, або підключення MySQL / Redis. Проблема полягає в тому, що стек помилок не допомагає мені визначити проблему з кодом. Ось вихід журналу:

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

Як я можу знати, що викликає це? Як отримати більше помилок?

Гаразд, не дуже багатослівний, але ось стек-трек з Лонгхоном:

    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] ] } }

Тут я подаю файл політики політики флеш-сокета:

    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)

Чи може це бути причиною?


3
@GottZ, можливо, це може допомогти (розмовляв із тим, хто працює у вузлі js) gist.github.com/samsonradu/1b0c6feb438f5a53e30e . Я сьогодні розгорню обробник socket.error та повідомляю вас.
Самсон

1
@Gottz ручки socket.error не допомагають, але process.on ('uncaughtException') виявляє помилку. Ось console.log помилки: {[Помилка: прочитати ECONNRESET] код: 'ECONNRESET', errno: 'ECONNRESET', syscall: 'read'}
Самсон

1
ECONNRESET може виникнути через мережеву проблему. Як ви знаєте, неможливо зловити всі винятки при тестуванні. Деякі з’являться на вашому виробничому сервері. Вам доведеться зробити ваш сервер надійним. Ви можете обробити видалення сеансу, використовуючи Redis як сховище. Це робить ваші сеанси збереженими навіть після того, як ваш сервер вузлів вийде з ладу.
user568109

1
Чому це пов’язано із видаленням сеансу? З ними все одно поводиться Редіс.
Самсон

3
У вас є щонайменше одне прослуховування TCP-сокетів, у яких не встановлено обробник. Отже, зараз час перевірити, де це: D
Мосс

Відповіді:


253

Ви, можливо, вже здогадалися: це помилка підключення.

"ECONNRESET" означає, що інша сторона розмови TCP різко закрила свій кінець з'єднання. Це, швидше за все, пов’язано з однією або кількома помилками протоколу програми. Ви можете подивитися журнали сервера API, щоб побачити, чи скаржиться він на щось.

Але оскільки ви також шукаєте спосіб перевірити помилку та, можливо, налагодити проблему, вам слід поглянути на " Як відлагодити помилку відключення сокета в NodeJS? ", Яка була розміщена в stackoverflow стосовно подібного питання.

Швидке та брудне рішення для розробки :

Використовуючи longjohn , ви отримуєте довгі сліди стека, які будуть містити операції асинхронізації.

Чисте і правильне рішення : Технічно в вузлі кожен раз, коли ви випускаєте 'error'подію, і ніхто її не слухає, вона кинеться . Щоб воно не кидалося, покладіть на нього слухача і обробіть його самостійно. Таким чином ви можете зафіксувати помилку за допомогою додаткової інформації.

Щоб мати одного слухача для групи дзвінків, ви можете використовувати домени, а також вловлювати інші помилки під час виконання. Переконайтеся, що кожна операція асинхронізації, пов'язана з http (Сервер / Клієнт), перебуває у різному доменному контексті порівняно з іншими частинами коду, домен автоматично прослуховує errorподії та поширюватиме його на власний обробник. Тож ви слухаєте лише цей обробник і отримуєте дані про помилки. Ви також отримуєте більше інформації безкоштовно.

EDIT (2013-07-22)

Як я писав вище:

"ECONNRESET" означає, що інша сторона розмови TCP різко закрила свій кінець з'єднання. Це, швидше за все, пов’язано з однією або кількома помилками протоколу програми. Ви можете подивитися журнали сервера API, щоб побачити, чи скаржиться він на щось.

Що також може бути так: у випадковий час інша сторона перевантажена і в результаті просто вбиває з'єднання. Якщо це так, залежить від того, до чого саме ви підключаєтесь…

Але одне точно: ви дійсно маєте помилку читання на своєму TCP-з’єднанні, що викликає виняток. Ви можете бачити це, переглядаючи код помилки, який ви опублікували у своїй редакції, який це підтверджує.


Це не повинно означати "різко закритий". Зазвичай це результат запису на з'єднання, яке одноліткові вже нормально закрили. Це призведе до видачі RST.
Маркіз Лорнський

1
@EJP Була вагома причина, чому я написав "круто". Помилка (не попереджає) повідомляє, що з'єднання було скинуто одноранговим. Існуюче з'єднання було примусово закрите віддаленим аналогом. Примусове закриття різко з несподіваного! (Зазвичай це призводить до того, що додаток однорангового зв’язку на віддаленій машині раптово зупиняється, перезавантажується машина або використовується «жорсткий закриття» у віддаленій розетці. Ця помилка може також призвести до того, що з'єднання було розірвано через активність «зберігання в живих» виявлення несправності під час виконання однієї чи декількох операцій ... ці операції та наступні операції будуть виходити з ладу.)
e-sushi

2
Я отримую цю помилку, коли пакетно надсилаю близько 100 дзвінків API поблизу браузера (Chrome) на тестування. Я думаю, що Chrome повинен перевантажуватись і вбивати деякі з'єднання ... @Samson - що не так з обробкою кожного запиту у власному домені та ловом помилок домену без перезавантаження сервера?
супершнє

2
@supershnee Ви майже завжди повинні перезавантажувати свій сервер після невдалого винятку, оскільки ваші дані, додаток та node.js перебувають у невідомому стані. Продовження після винятку створює ваші дані. Якщо ви хочете дізнатися більше, ознайомтеся з документами Node у процесі чи документами Node у доменах .
c1moore

39

Простий сервер tcp, який я мав для обслуговування файлу політики флеш-файлів, викликав це. Тепер я можу зрозуміти помилку за допомогою обробника:

# 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)

2
Чи є щось не так з кодом? Чи повинен я перевірити, чи розетка піддається запису перед тим, як писати?
Самсон

До, не бачив, що ти вже знайшов рішення до того, як я опублікував майже те ж саме :) Хоча щодо вашого запитання, навіть якщо ви перевірите, чи розширюється сокет, він може бути не тоді, коли ви напишете в нього мікросекунди пізніше і все-таки кинуло б помилку, тож це "шлях", на який слід бути впевненим.
Йоахім Ісакссон

добре, а чи є безпечний вихід, якщо це? як socket.close () всередині обробника помилок? тому що я думаю, що моє навантаження на процесор зростає після цих помилок (не впевнений)
Самсон

2
Я завжди дзвонив socket.destroy()в обробник помилок, щоб переконатися. На жаль, я не можу знайти документацію, чи потрібна вона, але вона не видає помилки для цього.
Йоахім Ісакссон

socket.destroy () врятував мені день, як би це не працювало !! Дякую!
Фірас Абд Алрахман

27

У мене була подібна проблема, коли програми почали помилки після оновлення Node. Я вважаю, що це можна простежити до випуску Node v0.9.10 цього елемента:

  • сітка: не пригнічуйте ECONNRESET (Ben Noordhuis)

Попередні версії не помилялися б від перерв у клієнта. Розрив з'єднання з клієнтом видає помилку ECONNRESET в Node. Я вважаю, що це призначений функціонал для Node, тому виправлення (принаймні для мене) полягало в тому, щоб виправити помилку, яку я вважаю, що ви робили за винятками UnCaught. Хоча я обробляю це в обробці net.socket.

Ви можете продемонструвати це:

Створіть простий сервер socket і отримайте Node v0.9.9 та v0.9.10.

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

Запустіть його, використовуючи v0.9.9, а потім спробуйте встановити FTP на цей сервер. Я використовую FTP та порт 21 лише тому, що я перебуваю у Windows та маю FTP-клієнт, але жоден клієнт telnet не підходить.

Тоді з боку клієнта просто перервіть з'єднання. (Я просто роблю Ctrl-C)

Ви повинні бачити NO ERROR при використанні Node v0.9.9 та ERROR при використанні Node v.0.9.10 і вище.

У виробництві я використовую v.0.10. щось і все одно дає помилку. Знову ж таки, я думаю, що це призначено, і рішення полягає в тому, щоб вирішити помилку у вашому коді.


3
Дякую, я сам це прибив! Важливо не дозволяти помилкам поширюватися на uncaughtException, оскільки це робить весь додаток нестабільним. Наприклад, після виявлення 10 помилок ECONNRESET, сервер іноді не реагував (просто замерз і не впорався з будь-якими з'єднаннями)
Самсон

Також знала про зміну версії вузла, яка більше не придушувала помилку, але, побачивши так багато проблем, що з'являються та вирішуються кожною версією, я скоріше переходжу на останню. Я використовую V0.10.13 зараз btw
Самсон

16

Була така ж проблема і сьогодні. Після деяких досліджень я знайшов дуже корисний --abort-on-uncaught-exceptionваріант Node.js . Він не тільки забезпечує набагато більш багатослівний та корисний слід стека помилок, але також зберігає основний файл при збої програми, що дозволяє надалі налагоджувати.


4
дивно, що нова відповідь на це старе запитання повинна з’являтися, як я дивлюся - але це чудово, дякую
напівколонка

13

Я зіткнувся з тим же питанням, але я пом'якшив це, розмістивши:

server.timeout = 0;

раніше server.listen. serverтут є HTTP-сервер. Тимчасовий час очікування становить 2 хвилини відповідно до документації API .


5
Це не рішення, а швидше виправлення, яке порушить речі без помилки.
Нішант Годке

9

Інший можливий випадок (але рідкісний) може бути, якщо у вас є зв’язок між сервером і сервером server.maxConnections дуже низьке значення.

У основній lib net.js вузла він викличе, clientHandle.close()що також спричинить помилку ECONNRESET:

if (self.maxConnections && self._connections >= self.maxConnections) {
  clientHandle.close(); // causes ECONNRESET on the other end
  return;
}

Чудовий дзвінок, але maxConnectionsзначення за замовчуванням є Infinity. Це було б лише у випадку (як ви сказали), якщо ви явно перекрили це значення.
Гаджус

7

Так, ваше подання файлу політики безумовно може спричинити збій.

Щоб повторити, просто додайте затримку до свого коду:

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

… І використовувати telnet для підключення до порту. Якщо ви відключите telnet до закінчення строку затримки, ви отримаєте збій (випадок вимкнення), коли socket.write видасть помилку.

Щоб уникнути аварії тут, просто додайте оброблювач помилок перед читанням / записом сокета:

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

Якщо ви спробуєте роз'єднати вище, ви отримаєте повідомлення журналу замість аварії.

І коли ви закінчите, не забудьте зняти затримку.


6

Я також отримую помилку ECONNRESET під час своєї розробки, як я її вирішую, не використовуючи nodemon для запуску сервера, просто використовуйте"node server.js" для запуску сервера виправлену мою проблему.

Це дивно, але це працювало для мене, тепер я більше ніколи не бачу помилки ECONNRESET.


4

У мене була ця помилка і я зміг її вирішити після днів налагодження та аналізу:

моє рішення

Для мене VirtualBox (для Docker) була проблемою. У мене на VM було налаштовано переадресацію портів, і помилка сталася лише на переадресованому порту.

загальні висновки

Наступні зауваження можуть врятувати вам дні роботи, яку мені довелося вкласти:

  • Для мене проблема виникла лише при з'єднаннях з localhost до localhost на одному порту. -> перевірка зміни будь-якої з цих констант вирішує проблему.
  • Для мене проблема виникла лише на моїй машині -> нехай хтось інший спробує це.
  • Для мене проблема виникла лише через деякий час, і її неможливо було відтворити надійно
  • Мою проблему не вдалося перевірити жодним із вузлів або виразів (налагодження). -> не витрачайте на це часу

-> з'ясуйте, чи щось зіпсується з вашою мережею (-налаштуваннями), наприклад, VM, брандмауерами і т. д., це, мабуть, причина проблеми.


2

Я вирішив проблему, просто підключившись до іншої мережі . Це одна з можливих проблем.

Як обговорювалося вище, ECONNRESET означає, що розмова TCP різко закрила свій кінець з'єднання.

Можливо, ваше інтернет-з'єднання не дозволяє вам підключатися до деяких серверів. У моєму випадку я намагався підключитися до mLab (хмарний сервіс баз даних, який розміщує бази даних MongoDB). І мій провайдер блокує це.


Цей працював на мене, мій код, який працював чудово кілька годин тому, раптом перестав працювати, виявляється, зміна мережі спричинила проблеми
Aklank Jain

2

Я вирішив цю проблему:

  • Вимкнення мого підключення до wifi / ethernet та увімкнення.
  • Я набрав: npm updateу терміналі для оновлення npm.
  • Я спробував вийти з сеансу та знову увійти

Після цього я спробував ту саму команду npm, і добре, що вона була відпрацьована. Я не був впевнений, що це так просто.

Я використовую CENTOS 7


0

У мене була така ж проблема, і, здається, проблема була у версії Node.js.

Я встановив попередню версію Node.js (10.14.2), і все було нормально за допомогою nvm (дозволяють встановити кілька версій Node.js і швидко перейти з версії на іншу).

Це не "чисте" рішення, але воно може служити вам тимчасово.


0

Я щойно це зрозумів, принаймні в моєму випадку використання.

Я добирався ECONNRESET . Виявилося, що так, як мій клієнт був налаштований, він ударяв сервер за допомогою API виклику в тоні разів дуже швидко - і потрібно було лише один раз потрапити в кінцеву точку.

Коли я це виправив, помилка зникла.


-2

Спробуйте додати ці параметри до socket.io:

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

Сподіваюся, це допоможе тобі!

Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.