Я только начал пробовать node.js несколько дней назад. Я понял, что Node завершается всякий раз, когда в моей программе возникает необработанное исключение. Это отличается от обычного серверного контейнера, с которым я столкнулся, когда только рабочий поток умирает, когда возникают необработанные исключения, и контейнер все еще сможет получать запрос. Это поднимает несколько вопросов:
- Это
process.on('uncaughtException')
единственный эффективный способ защититься от этого? - Будет ли
process.on('uncaughtException')
перехватывать необработанное исключение и во время выполнения асинхронных процессов? - Есть ли уже созданный модуль (например, отправка электронной почты или запись в файл), который я мог бы использовать в случае неперехваченных исключений?
Я был бы признателен за любой указатель / статью, которая показала бы мне общие рекомендации по обработке необработанных исключений в node.js
try .. catch
и убедитесь, что это также сделано для всех ваших библиотекsetTimeout
тоsetInterval
или что-то в этом роде где-то глубоко, что не может быть поймано вашим кодом.Ответы:
Обновление: Joyent теперь имеет свой собственный гид . Следующая информация более краткая:
Безопасно «выбрасывать» ошибки
В идеале мы хотели бы как можно больше избегать неперехваченных ошибок, поэтому вместо того, чтобы буквально выдавать ошибку, мы можем вместо этого безопасно «выбросить» ошибку, используя один из следующих методов в зависимости от нашей архитектуры кода:
Для синхронного кода, если произошла ошибка, верните ошибку:
Для основанного на обратном вызове (т. Е. Асинхронного) кода первый аргумент обратного вызова:
err
если ошибка происходит,err
это ошибка, если ошибка не возникает, тоerr
естьnull
. Любые другие аргументы следуют заerr
аргументом:Для событийного кода, где ошибка может произойти где угодно, вместо того, чтобы выдать ошибку, запустите
error
событие вместо :Безопасно «ловить» ошибки
Иногда, тем не менее, может существовать код, который выдает ошибку, которая может привести к необработанному исключению и потенциальному сбою нашего приложения, если мы не перехватим его безопасно. В зависимости от нашей архитектуры кода мы можем использовать один из следующих методов, чтобы поймать его:
Когда мы знаем, где происходит ошибка, мы можем заключить этот раздел в домен node.js
Если мы знаем, где происходит ошибка, это синхронный код и по какой-либо причине не можем использовать домены (возможно, старая версия узла), мы можем использовать оператор try catch:
Однако будьте осторожны, чтобы не использовать их
try...catch
в асинхронном коде, так как асинхронно выданная ошибка не будет обнаружена:Если вы хотите работать
try..catch
в сочетании с асинхронным кодом, при запуске узла 7.4 или выше вы можете использоватьasync/await
нативно для написания своих асинхронных функций.Еще одна вещь, с которой следует быть осторожным,
try...catch
это риск обернуть ваш обратный вызов завершения вtry
оператор следующим образом:Это очень легко сделать, так как ваш код становится более сложным. Поэтому лучше всего использовать домены или возвращать ошибки, чтобы избежать (1) необработанных исключений в асинхронном коде (2) попытки перехвата перехвата, который вам не нужен. В языках, которые допускают правильную многопоточность вместо асинхронного стиля обработки событий в JavaScript, это не проблема.
Наконец, в случае, когда в месте, которое не было перенесено в домен или в оператор try catch, возникает неперехваченная ошибка, мы можем сделать так, чтобы наше приложение не аварийно завершало работу с помощью
uncaughtException
прослушивателя (однако это может привести приложение в неизвестное состояние. ):источник
try catch
? Как я хотел бы подтвердить это доказательствами. Также исправлен пример синхронизации.Ниже приводится краткое изложение различных источников по этой теме, включая пример кода и цитаты из отдельных сообщений в блоге. Полный список лучших практик можно найти здесь
Лучшие практики обработки ошибок Node.JS
Number1: Используйте обещания для асинхронной обработки ошибок
TL; DR: обработка асинхронных ошибок в стиле обратного вызова, вероятно, является самым быстрым путем в ад (пирамида гибели). Лучший подарок, который вы можете дать своему коду, - это использовать надежную библиотеку обещаний, которая предоставляет очень компактный и знакомый синтаксис кода, такой как try-catch
В противном случае: стиль обратного вызова Node.JS, функция (err, response), является многообещающим способом для необслуживаемого кода из-за сочетания обработки ошибок со случайным кодом, чрезмерного вложения и неуклюжих шаблонов кодирования.
Пример кода - хорошо
пример кода, анти-паттерн - обработка ошибок в стиле обратного вызова
Цитата блога: «У нас проблема с обещаниями» (из блога pouchdb заняла 11 место по ключевым словам «Узловые обещания»)
Number2: используйте только встроенный объект Error
TL; DR: довольно часто можно увидеть код, который выдает ошибки в виде строки или пользовательского типа - это усложняет логику обработки ошибок и взаимодействие между модулями. Вне зависимости от того, отклоняете ли вы обещание, генерируете исключение или генерируете ошибку - использование встроенного в объект Node.JS Error повышает единообразие и предотвращает потерю информации об ошибках.
В противном случае: при выполнении какого-либо модуля, будучи неуверенным, какой тип ошибок приходит взамен - намного сложнее рассуждать о наступающем исключении и обрабатывать его. Даже лучше, использование пользовательских типов для описания ошибок может привести к потере информации о критических ошибках, таких как трассировка стека!
Пример кода - делать все правильно
пример кода анти-паттерн
Цитата блога: «Строка не является ошибкой» (из блога devthought заняла 6 место по ключевым словам «Node.JS error object»)
Number3: Различают операционные и программистские ошибки
TL; DR: Операционные ошибки (например, API получил неверный ввод) относятся к известным случаям, когда влияние ошибки полностью понимается и может быть обработано вдумчиво. С другой стороны, ошибка программиста (например, попытка прочитать неопределенную переменную) относится к неизвестным ошибкам кода, которые требуют изящного перезапуска приложения.
В противном случае: вы всегда можете перезапустить приложение при появлении ошибки, но зачем подводить ~ 5000 онлайн-пользователей из-за незначительной и прогнозируемой ошибки (ошибка в работе)? обратное также не идеально - поддержание приложения в случае возникновения неизвестной проблемы (ошибка программиста) может привести к непредсказуемому поведению. Разграничение между ними позволяет действовать тактично и применять сбалансированный подход, основанный на данном контексте
Пример кода - делать все правильно
пример кода - пометка ошибки как действующей (доверенной)
Цитата блога : «В противном случае вы рискуете состоянием» (из отлаживаемого блога оценивается 3 по ключевым словам «Node.JS uncaught исключения»)
Number4: Обрабатывать ошибки централизованно, но не в промежуточном программном обеспечении
TL; DR: логика обработки ошибок, такая как почта для администратора и ведение журнала, должна быть заключена в выделенный и централизованный объект, который вызывают все конечные точки (например, промежуточное программное обеспечение Express, задания cron, модульное тестирование) при возникновении ошибки.
В противном случае: не обработка ошибок в одном месте приведет к дублированию кода и, возможно, к ошибкам, которые обрабатываются неправильно
Пример кода - типичный поток ошибок
Цитата блога: «Иногда нижние уровни не могут сделать ничего полезного, кроме как сообщить об ошибке своему вызывающему абоненту» (из блога Joyent оценивается 1 по ключевым словам «Обработка ошибок Node.JS»)
Number5: Ошибки API документа с использованием Swagger
TL; DR: пусть ваши вызывающие API знают, какие ошибки могут прийти взамен, чтобы они могли обрабатывать их вдумчиво без сбоев Обычно это делается с помощью каркасов документации REST API, таких как Swagger
В противном случае: клиент API может принять решение о сбое и перезапуске только потому, что он получил ошибку, которую он не мог понять. Примечание: вызывающим абонентом вашего API может быть вы (очень типично в среде микросервисов)
Цитата блога: «Вы должны сообщить своим абонентам, какие ошибки могут произойти» (из блога Joyent занял 1 место по ключевым словам «Node.JS logging»)
Number6: Прервите процесс изящно, когда незнакомец приезжает в город
TL; DR: при возникновении неизвестной ошибки (ошибка разработчика, см. Рекомендацию № 3) - существует неопределенность в отношении работоспособности приложения. Обычная практика предполагает осторожный перезапуск процесса с использованием инструмента «перезапуска», такого как Forever и PM2.
В противном случае: когда обнаруживается незнакомое исключение, некоторый объект может находиться в неисправном состоянии (например, источник событий, который используется глобально и больше не генерирует события из-за некоторого внутреннего сбоя), и все будущие запросы могут завершаться сбоем или вести себя безумно
Пример кода - решение о сбое
Цитата блога: «Есть три школы мысли об обработке ошибок» (из блога jsrecipes)
Number7: Используйте зрелый регистратор, чтобы увеличить видимость ошибок
TL; DR: набор зрелых инструментов ведения журналов, таких как Winston, Bunyan или Log4J, ускорит обнаружение и понимание ошибок. Так что забудьте о console.log.
В противном случае: просмотр через console.logs или вручную через грязный текстовый файл без запросов инструментов или приличного средства просмотра журнала может занять вас на работе до поздна
Пример кода - Winston logger в действии
Цитата блога: «Давайте определим несколько требований (для регистратора):» (Из блога strongblog)
Number8: обнаружение ошибок и простоев с использованием продуктов APM
TL; DR: продукты для мониторинга и производительности (также известные как APM) проактивно измеряют вашу кодовую базу или API, чтобы они могли автоматически подсвечивать ошибки, сбои и медленные части, которые вы пропустили
В противном случае: вы можете потратить огромные усилия на измерение производительности и времени простоя API, возможно, вы никогда не узнаете, какие ваши самые медленные части кода в сценарии реального мира и как они влияют на UX
Цитата блога: «Сегменты продуктов APM» (из блога Йони Голдберг)
Выше приведен сокращенный вариант - смотрите здесь больше лучших практик и примеров
источник
Вы можете поймать неисследованные исключения, но это ограниченное использование. См. Http://debuggable.com/posts/node-js-dealing-with-uncaught-exceptions:4c933d54-1428-443c-928d-4e1ecbdd56cb
monit
,forever
илиupstart
может использоваться для перезапуска процесса узла, когда он падает. Лучше всего надеяться на плавное завершение работы (например, сохранить все данные в памяти в обработчике необработанных исключений).источник
Error
делает возвращаемое значение полиморфным, что излишне запутывает семантику функции. Кроме того, плавание на 0 уже обработано в JavaScript, даваяInfinity
,-Infinity
илиNaN
, где это значениеtypeof === 'number'
. Их можно проверить с помощью!isFinite(value)
. В общем, я бы рекомендовал никогда не возвращать ошибку из функции. Лучше с точки зрения разборчивости кода и обслуживания, чтобы бросить или вернуть специальное неполиморфное значение с согласованной семантикой.Домены nodejs - самый современный способ обработки ошибок в nodejs. Домены могут захватывать как ошибки / другие события, так и традиционно выбрасываемые объекты. Домены также предоставляют функциональность для обработки обратных вызовов с ошибкой, переданной в качестве первого аргумента через метод перехвата.
Как и при обычной обработке ошибок в стиле try / catch, обычно лучше выбрасывать ошибки, когда они возникают, и блокировать области, где вы хотите изолировать ошибки от воздействия на остальную часть кода. Способ «заблокировать» эти области - вызвать domain.run с функцией в виде блока изолированного кода.
В синхронном коде достаточно вышеприведенного - когда происходит ошибка, вы либо пропускаете ее, либо перехватываете и обрабатываете там, возвращая любые данные, которые вам нужно вернуть.
Когда ошибка возникает в асинхронном обратном вызове, вам также необходимо иметь возможность полностью обработать откат данных (общее состояние, внешние данные, такие как базы данных и т. Д.). ИЛИ вы должны установить что-то, чтобы указать, что произошло исключение - где бы вы ни заботились об этом флаге, вы должны ждать завершения обратного вызова.
Кое-что из вышеприведенного кода выглядит ужасно, но вы можете создать для себя шаблоны, чтобы сделать его красивее, например:
ОБНОВЛЕНИЕ (2013-09):
Выше я использую будущее, которое подразумевает семантику волокон , которые позволяют вам ждать фьючерсов в потоке. Это на самом деле позволяет вам использовать традиционные блоки try-catch для всего - что я считаю лучшим способом. Тем не менее, вы не всегда можете сделать это (например, в браузере) ...
Есть также фьючерсы, которые не требуют семантики волокон (которые затем работают с обычным JavaScript-браузером). Их можно назвать фьючерсами, обещаниями или отсрочками (далее я просто буду ссылаться на фьючерсы). Фьючерсные библиотеки простого старого JavaScript позволяют распространять ошибки между фьючерсами. Только некоторые из этих библиотек позволяют правильно обрабатывать любое будущее, поэтому будьте осторожны.
Пример:
Это имитирует нормальную попытку, даже если части асинхронны. Это напечатало бы:
Обратите внимание, что он не печатает «3», потому что было сгенерировано исключение, которое прерывает этот поток.
Взгляните на обещания синей птицы:
Обратите внимание, что я не нашел много других библиотек, кроме этих, которые правильно обрабатывают выданные исключения. Например, jQuery deferred - обработчик «fail» никогда не получит исключение, генерирующее обработчик «then», который, на мой взгляд, нарушает условия сделки.
источник
Я писал об этом недавно на http://snmaynard.com/2012/12/21/node-error-handling/ . Новая функция узла в версии 0.8 - это домены, которые позволяют объединить все формы обработки ошибок в одну более простую форму управления. Вы можете прочитать о них в моем посте.
Вы также можете использовать что-то вроде Bugsnag для отслеживания ваших необученных исключений и получать уведомления по электронной почте, в чате или иметь билет, созданный для необученного исключения (я являюсь соучредителем Bugsnag).
источник
Я просто хотел бы добавить, что библиотека Step.js помогает вам обрабатывать исключения, всегда передавая их функции следующего шага. Поэтому в качестве последнего шага вы можете использовать функцию, которая проверяет наличие ошибок на любом из предыдущих шагов. Такой подход может значительно упростить вашу обработку ошибок.
Ниже приводится цитата со страницы GitHub:
Кроме того, вы можете использовать Step для управления выполнением сценариев, чтобы иметь раздел очистки в качестве последнего шага. Например, если вы хотите написать скрипт сборки в Node и сообщить, сколько времени потребовалось на запись, последний шаг может сделать это (вместо того, чтобы пытаться откопать последний обратный вызов).
источник
Один пример, где использование try-catch может быть уместным, - это использование цикла forEach. Это синхронно, но в то же время вы не можете просто использовать оператор return во внутренней области видимости. Вместо этого можно использовать подход try and catch для возврата объекта Error в соответствующей области видимости. Рассматривать:
Это комбинация подходов, описанных @balupton выше.
источник
После прочтения этого поста некоторое время назад мне стало интересно, безопасно ли использовать домены для обработки исключений на уровне API / функции. Я хотел использовать их для упрощения кода обработки исключений в каждой написанной мной асинхронной функции. Меня беспокоило, что использование нового домена для каждой функции приведет к значительным накладным расходам. Моя домашняя работа, кажется, указывает на то, что накладные расходы минимальны, и что в некоторых случаях производительность на доменах лучше, чем на try catch.
http://www.lighthouselogic.com/#/using-a-new-domain-for-each-async-function-in-node/
источник
Захват ошибок очень хорошо обсуждался здесь, но стоит не забывать регистрировать ошибки где-нибудь, чтобы вы могли их просмотреть и исправить.
Bunyan - это популярный каркас журналирования для NodeJS - он поддерживает запись в кучу разных выходных мест, что делает его полезным для локальной отладки, если вы избегаете console.log. В обработчике ошибок вашего домена вы можете выложить ошибку в файл журнала.
Это может занять много времени, если у вас есть много ошибок и / или серверов для проверки, поэтому, возможно, стоит поискать такой инструмент, как Raygun (отказ от ответственности, я работаю в Raygun), чтобы сгруппировать ошибки вместе - или использовать их вместе. Если вы решили использовать Raygun в качестве инструмента, его тоже довольно легко настроить
При использовании такого инструмента, как PM2 или навсегда, ваше приложение должно быть способно аварийно завершить работу, выйти из системы и перезагрузиться без каких-либо серьезных проблем.
источник
Если вы хотите использовать Сервисы в Ubuntu (Upstart): Узел как сервис в Ubuntu 11.04 с upstart, monit и forever.js
источник