Подтвердить что ты не робот

Почему исключение вызывает утечку ресурсов в Node.js?

Если вы посмотрите на начало документа Node.js для доменов, в нем говорится:

По самой природе того, как работает бросок в JavaScript, почти никогда не существует способа безопасно "забрать, где вы остановились", без утечки ссылок или создания какого-либо другого хрупкого состояния undefined.

Опять же, в примере кода, который он дает в первом разделе, он говорит:

Хотя мы предотвратили внезапный перезапуск процесса, у нас протекают ресурсы, такие как сумасшедшие

Я хотел бы понять, почему это так? Какие ресурсы протекают? Они рекомендуют использовать домены только для обнаружения ошибок и безопасного завершения процесса. Это проблема со всеми исключениями, а не только при работе с доменами? Является ли плохой практикой бросать и перехватывать исключения в Javascript? Я знаю, что это общий шаблон в Python.

ИЗМЕНИТЬ

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

Единственная причина, по которой я могу представить с помощью Javascript, - это то, что если выброс исключения хранит ссылки на переменные в области, где было выбрано исключение (и, возможно, вещи в стеке вызовов), таким образом сохраняя ссылки вокруг, а затем объект исключения сохраняется вокруг и никогда не очищается. Если упомянутые утечки не являются ресурсами, внутренними для двигателя.

UPDATE

Я написал блог, объясняющий ответ на это немного лучше. Проверьте это

4b9b3361

Ответ 1

Неожиданными исключениями являются те, о которых вам нужно беспокоиться. Если вы недостаточно осведомлены о состоянии приложения для добавления обработки для конкретного исключения и управления любой необходимой очисткой состояния, то по определению состояние вашего приложения undefined и непознаваемо, и вполне возможно, что там вещи, висящие вокруг, этого не должно быть. Это не просто утечка памяти, о которой вы должны беспокоиться. Неизвестное состояние приложения может вызвать непредсказуемое и нежелательное поведение приложения (например, доставка неверного результата - частично визуализированный шаблон или неполный результат вычисления или, что еще хуже, условие, при котором каждый последующий вывод неверен). Поэтому важно выйти из процесса, когда возникает необработанное исключение. Это дает вашему приложению возможность восстановить себя.

Исключения случаются, и это прекрасно. Прими это. Выключите этот процесс и используйте что-то вроде Forever, чтобы обнаружить его и снова установить на дорожку. Кластеры и домены тоже замечательные. Текст, который вы читали, не является предостережением против бросания исключений или продолжения процесса, когда вы обрабатывали исключение, которое вы ожидали, - это предостережение против того, чтобы процесс работал, когда происходят неожиданные исключения.

Ответ 2

Я думаю, когда они сказали "мы утечка ресурсов", они действительно означали "мы могли утечка ресурсов". Если http.createServer обрабатывает исключения соответствующим образом, потоки и сокеты не должны просачиваться. Тем не менее, они, конечно, могут быть, если они не обрабатывают вещи должным образом. В общем случае вы никогда не знаете, действительно ли что-то правильно обрабатывает ошибки.

Я думаю, что они ошибаются/очень вводят в заблуждение, когда они говорят: "По характеру того, как работает бросок в JavaScript, почти никогда не существует безопасного способа...". Не должно быть ничего о том, как работает бросок в Javascript (и на других языках), что делает его небезопасным. Также нет ничего о том, как работает бросок/улов в целом, что делает его небезопасным - если, конечно, вы не используете их неправильно.

То, что они должны были сказать, заключается в том, что исключительные случаи (независимо от того, используются ли исключения), должны быть обработаны надлежащим образом. Существует несколько разных категорий:

а. Государство

  • Исключения, которые происходят во внешнем состоянии (запись базы данных, выход файла и т.д.), находятся в переходном состоянии.
  • Исключения, которые происходят во время общей памяти, находятся в переходном состоянии
  • Исключения, где только локальные переменные могут находиться в переходном состоянии

В. Обратимость

  • Реверсивное/обратимое состояние (например, откаты базы данных)
  • Необратимое состояние (Утерянные данные, неизвестные способы обращения или запрет на обратное)

С. Критичность данных

  • Данные могут быть отменены.
  • Данные должны использоваться (даже если они повреждены)

Независимо от типа состояния, с которым вы возитесь, если вы можете изменить его, вы должны сделать это, и вы настроены. Проблема - необратимое состояние. Если вы можете уничтожить поврежденные данные (или карантин для отдельной проверки), это лучший шаг для необратимого состояния. Это делается автоматически для локальных переменных, когда генерируется исключение, поэтому исключения превосходят обработку ошибок в чисто функциональном коде (т.е. функции без возможных побочных эффектов). Аналогично, любое общее состояние или внешнее состояние должно быть удалено, если это приемлемо. В случае общего состояния либо бросать исключения, пока это разделяемое состояние не становится локальным, а очищается путем разворачивания стека (статически или через GC) или перезапускает программу (я читал людей, предлагающих использовать что-то как nodejitsu навсегда). Для внешнего состояния это, вероятно, сложнее.

Последний случай - когда данные критические. Ну, тогда вам придется жить с ошибками, которые вы создали. Каждый человек должен иметь дело с ошибками, но это самое худшее, когда ваши ошибки связаны с поврежденными данными. Для этого обычно требуется ручное вмешательство (восстановление потерянных/поврежденных данных, выборочная обрезка и т.д.). Обработка исключений не приведет вас к концу в последнем случае.

Я написал аналогичный ответ, связанный с тем, как обрабатывать сбой в середине операции в различных случаях в контексте нескольких обновлений для некоторого хранилища данных: fooobar.com/questions/237546/...

Ответ 3

Взятие образца из документации node.js:

var d = require('domain').create();
d.on('error', function(er) {
  // The error won't crash the process, but what it does is worse!
  // Though we've prevented abrupt process restarting, we are leaking
  // resources like crazy if this ever happens.
  // This is no better than process.on('uncaughtException')!
  console.log('error, but oh well', er.message);
});
d.run(function() {
  require('http').createServer(function(req, res) {
    handleRequest(req, res);
  }).listen(PORT);
});

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

"Просочился" в том смысле, что вы закончили обработку запроса без очистки после этого. В конце концов соединение будет тайм-аут и закрыть сокет, но если ваш сервер находится под большой нагрузкой, он может закончиться сокетами до того, как это произойдет.

В зависимости от того, что вы делаете в handleRequest, вы также можете пропускать файлы, соединения с базами данных, прослушиватели событий и т.д.

В идеале вы должны обрабатывать свои исключения, чтобы вы могли очистить их после.