Как мне отладить ошибку ECONNRESET в Node.js?


288

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

Проблема в том, что перезапуск Express выбивает моих пользователей из их комнат, и никто не хочет этого.

Веб-сервер прокси HAProxy. Нет проблем со стабильностью сокетов, только при использовании веб-сокетов и транспортов flashsockets. Я не могу воспроизвести это специально.

Это ошибка с узлом 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

РЕДАКТИРОВАТЬ (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)

Как я знаю, что вызывает это? Как я могу получить больше от ошибки?

Хорошо, не очень многословно, но вот трассировка стека с 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] ] } }

Здесь я использую файл политики флэш-сокета:

    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
@ Готц обрабатывает сокет socket.error, но это не помогает, но process.on ('uncaughtException') ловит ошибку. Вот файл console.log об ошибке: {[Ошибка: прочитать ECONNRESET] код: 'ECONNRESET', errno: 'ECONNRESET', syscall: 'read'}
Самсон

1
ECONNRESET может быть из-за проблем с сетью. Как известно, невозможно отловить все исключения при тестировании. Некоторые будут отображаться на вашем рабочем сервере. Вы должны будете сделать свой сервер надежным. Вы можете обработать удаление сеанса, используя Redis в качестве хранилища. Это заставляет ваши сеансы сохраняться даже после того, как ваш сервер узла выходит из строя.
user568109

1
Почему это связано с удалением сессии? Они все равно обрабатываются Redis.
Самсон

3
У вас есть хотя бы один прослушивающий сокет TCP, для которого не установлен обработчик. Так что теперь пришло время проверить, где это находится: D
Мосс

Ответы:


253

Возможно, вы уже догадались: это ошибка соединения.

«ECONNRESET» означает, что другая сторона диалога TCP внезапно закрыла свой конец соединения. Скорее всего, это связано с одной или несколькими ошибками протокола приложения. Вы можете просмотреть журналы сервера API, чтобы увидеть, если он жалуется на что-то.

Но так как вы также ищете способ проверить ошибку и, возможно, устранить ее, вы должны взглянуть на « Как отладить ошибку зависания сокета в NodeJS? », Которая была опубликована в stackoverflow по аналогичному вопросу.

Быстрое и грязное решение для разработки :

Используйте longjohn , вы получите длинные трассировки стека, которые будут содержать асинхронные операции.

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

Чтобы иметь одного слушателя для группы вызовов, вы можете использовать домены, а также ловить другие ошибки во время выполнения. Убедитесь, что каждая асинхронная операция, относящаяся к http (сервер / клиент), находится в различном контексте домена, по сравнению с другими частями кода, домен автоматически прослушивает errorсобытия и передает его своему собственному обработчику. Таким образом, вы только слушаете этот обработчик и получаете данные об ошибках. Вы также получите больше информации бесплатно.

РЕДАКТИРОВАТЬ (2013-07-22)

Как я уже писал выше:

«ECONNRESET» означает, что другая сторона диалога TCP внезапно закрыла свой конец соединения. Скорее всего, это связано с одной или несколькими ошибками протокола приложения. Вы можете просмотреть журналы сервера API, чтобы увидеть, если он жалуется на что-то.

Что также может иметь место: в случайное время другая сторона перегружается и в результате просто разрывает соединение. Если это так, зависит от того, к чему именно вы подключаетесь ...

Но одна вещь наверняка: у вас действительно есть ошибка чтения на вашем соединении TCP, которое вызывает исключение. Это можно увидеть, посмотрев код ошибки, который вы опубликовали в своем редактировании, что подтверждает это.


Это не должно означать «внезапно закрытый». Обычно это происходит из-за записи в соединение, которое одноранговый узел уже нормально закрыл. Это заставит это выпустить RST.
маркиз Лорн

1
@EJP Была веская причина, почему я написал «внезапно». Ошибка (не предупреждение) указывает, что соединение было сброшено узлом. Существующее соединение было принудительно закрыто удаленным узлом. Принудительное закрытие внезапно, так как неожиданно! (Обычно это происходит, если одноранговое приложение на удаленной машине внезапно останавливается, машина перезагружается или одноранговое приложение использует «жесткое закрытие» на удаленном сокете. Эта ошибка также может произойти, если соединение было разорвано из-за активности «keep-alive» обнаружение сбоя во время выполнения одной или нескольких операций… эти операции и последующие операции завершатся неудачей.)
e-sushi

2
Я получаю эту ошибку, когда я посылаю около 100 вызовов API одновременно из браузера (Chrome) для тестирования. Я полагаю, что Chrome должен затем быть перегружен и прервать некоторые соединения ... @ Самсон - что не так с обработкой каждого запроса в его собственном домене и перехватом доменных ошибок без перезапуска сервера?
супершнее

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 () внутри обработчика ошибок? потому что я думаю, что моя загрузка процессора увеличивается после этих ошибок (не уверен)
Samson

2
Я всегда вызывал socket.destroy()обработчик ошибок, чтобы убедиться. К сожалению, я не могу найти документацию, требуется ли это, но это не выдает ошибку, чтобы сделать это.
Иоахим Исакссон

socket.destroy () спас мой день, что бы это ни работало !! Спасибо!
Фирас Абд Алрахман

27

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

  • net: не подавлять ECONNRESET (Бен Нордхьюс)

Предыдущие версии не допускали ошибок при сбоях со стороны клиента. Разрыв соединения с клиентом выдает ошибку ECONNRESET в узле. Я считаю, что это предназначенная функциональность для Node, поэтому исправление (по крайней мере для меня) заключалось в обработке ошибки, которую, я полагаю, вы сделали в исключениях UnCaught. Хотя я справляюсь с этим в обработчике net.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 сервер иногда зависал (просто завис и не обрабатывал какие-либо соединения)
Samson

Также знал об изменении версии узла, которое больше не подавляло ошибку, но, увидев так много проблем и решаемых в каждой версии, я бы предпочел перейти на последнюю. Я использую V0.10.13 сейчас, кстати
Samson

16

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


4
странно, что новый ответ на этот старый вопрос должен появиться, когда я смотрю - но это здорово, спасибо
точка с запятой

13

Я столкнулся с той же проблемой, но я смягчил ее, разместив:

server.timeout = 0;

перед тем server.listen. serverздесь HTTP-сервер Время ожидания по умолчанию составляет 2 минуты согласно документации API .


5
Это не решение, а скорее быстрое исправление, которое сломает вещи без ошибки.
Nishant Ghodke

9

Другой возможный случай (но редкий) может быть, если у вас есть связь между серверами и вы установили server.maxConnectionsочень низкое значение.

В ядре lib lib net.js это вызовет, clientHandle.close()что также приведет к ошибке ECONNRESET:

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

Отличный звонок, но maxConnectionsзначение по умолчанию Infinity. Это будет иметь место только в том случае (как вы сказали), если вы явно изменили это значение.
Gajus

7

Да, ваша подача файла политики может определенно вызвать сбой.

Повторим, просто добавьте задержку в ваш код:

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

… И используйте telnetдля подключения к порту. Если вы отключите telnet до того, как истечет время задержки, вы получите сбой (исключение uncaught), когда 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) был проблемой. На моей виртуальной машине настроена переадресация портов, и ошибка произошла только на перенаправленном порту.

общие выводы

Следующие наблюдения могут сэкономить вам дни работы, которые мне пришлось потратить:

  • Для меня проблема возникла только при подключении от localhost к localhost на одном порту. -> проверка изменения любой из этих констант решает проблему.
  • Для меня проблема возникла только на моей машине -> пусть кто-то еще попробует.
  • Для меня проблема возникла только через некоторое время и не может быть надежно воспроизведена
  • Моя проблема не может быть проверена ни одним из узлов или выражений (отладки). -> не трать время на это

-> выяснить, если что-то не так с вашей сетью (-настройки), такие как виртуальные машины, брандмауэры и т. д., это, вероятно, причина проблемы


2

Я решил проблему, просто подключившись к другой сети . Это одна из возможных проблем.

Как обсуждалось выше, ECONNRESET означает, что диалог TCP внезапно закрыл свой конец соединения.

Возможно, ваше интернет-соединение не позволяет подключиться к некоторым серверам. В моем случае я пытался подключиться к mLab (облачной службе баз данных, в которой размещены базы данных MongoDB). И мой провайдер блокирует это.


Этот работает для меня, мой код, который работал нормально несколько часов назад, внезапно перестал работать, оказывается, смена сети вызвала проблему
Aklank Jain

2

Я решил эту проблему:

  • Отключить соединение Wi-Fi / 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.