Pull to refresh

Comments 11

Хорошо-бы еще добавить nodejs.org/api/process.html#process_event_uncaughtexception про перехват глобальных исключений, где можно также получить и код возврата — а не уронив активный инстанц нода (не забываем про модуль cluster)
И по поводу кода — для синхронных функций принято выбрасывать exception для try/catch, а для асинхронных возвращять результат вида prototype function(err=true, object=ExceptionObject)
Также в node.js+express есть одна особенность — если Вы НЕ уверены в том что Вам всегда завершится удачно — то нужно на каждое подключение в текущем контексте создавать таймер (обычно на 15секунд) — который автоматически закроет соединение при бездействии — тем самым очищая хвосты дескрипторов (актуально при больших нагрузках).
То что Вы описали — набор совершенно разных тематик. Не для одной статьи точно. В данной статье рассматривался именно способ обработки ошибок из асинхронных вызовов без потери контекста запроса и без ручной передачи ошибки наверх по стеку вызовов. Поэтому ни process.on('uncaughtException') ни возврат ошибки первым параметров в калбек тут не подходят. Об этом я упомянул в статье.

Самый простой пример, когда описанный в статье метод актуален:
1. Поступает запрос на страницу новостей
2. Контроллер запрашивает у модели новостей список первой страницы новостей
3. После того как список получен другой метод модели должен сопоставить каждой новости автора (они хранятся в отдельной таблице)
4. Во время получения информации об одном из авторов падает СУБД и запрос выполняется с ошибкой.

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

В случае с передачей ошибки первым параметром, нам придётся сначала вернуть её в в калбек функции получения авторов, потом вернуть её оттуда в функцию получения новостей, оттуда уже в функцию контроллера, в которой есть контекст ответа и уже оттуда мы можем вызвать next(err) и передать её обработчику ошибок. Очень часто бывает что стек вызовов ещё больше и появляется куча однотипного кода.

Описанный в статье метод позволяет избавиться от ручной передачи ошибки. Достаточно сделать throw err; там где она произошла и ошибка будет передана в обработчик с сохранением контекста запроса.

У меня вот давно возник вопрос. Какие плюсы есть у NodeJS по сравнению с Java?(тут намеренно сравнение инструмента и языка программирования, но можно взять например Netty чтобы было всё честно). Ответьте мне кто-нибудь пожалуйста.
Это очень холиварный вопрос. Серебряной пули не существует. У каждой технологии есть свои плюсы и минусы. Важно только то, как их использовать. На любой озвученный плюс найдётся Java/Python/Erlang/Ruby/C# программист, который скажет, что на %language_name% это можно сделать ещё лучше. Смысла нет это обсуждать.
У нас с Вами сходятся интересы :)
habrahabr.ru/company/alawar/blog/158905/

Раз уж вы работали с доменами, не подскажете, как можно создать 2 вложенных домена, и сделать rethrow, чтобы ошибка попала из обработчика ошибок внутреннего в обработчик ошибок внешнего? Я пытался с ними экспериментировать, но с некоторыми вопросами так и не разобрался.
Я не задавался вопросом вложенных доменов. Для решения необходимых задач пока хватает описанного в статье метода. Сейчас ради интереса попробовал — с ходу оно не заработало. uncaughtException работает, но в документации этот механизм не рекомендуют использовать вообще. Как рабочий вариант — вызывать topDomain.emit('error', err) внутри функции обработки ошибки вложенного domain. Но вообще, вкладывать Domain друг в друга, наверное, не самая лучшая идея. По крайней мере для этого сначала нужно полностью разобраться в их внутреннем устройстве, чтобы быть уверенным, что это не окажет влияния на производительность.
Ну просто вложенные домены — это прямой аналог try-catch блоков.
Ну и модули control-block и trycatch, которые решают сходные задачи, вполне себе переносят вложенность.
Я протестировал скорость работы модуля trycatch, на простейшем примере он работает на 20-30% медленнее чем вариант с Domain. При этом вариант с Domain работает на 7-10% медленнее варианта без обработчика ошибок вообще. И это на простейшем примере. trycatch работает очень хардкорно. Он оборачивает все системные методы в свои обработки и следит за стеком выполнения. Это очень медленные операции. К сожалению, пока нет времени протестировать на более сложном примере, но есть мнение что разрыв в скорости будет увеличиваться с возрастанием кол-ва операций. Плюс ко всему цена ошибки в стороннем модуле, который изменяет поведение всех важнейших методов очень высока.

control-block не подходит для решения поставленной задачи, т.к. требует дополнительно оборачивать калбеки.

Коды тестов:

Тест без обработки ошибок (эталон)
var
        connect = require('connect');

var app = connect()
        .use(function(req, res){
                process.nextTick(function() {
                        res.end('Hello from Connect!');
                });
        })
        .use(function(err, req, res, next) {
                res.end(err.message);
        });

app.listen(3131);



Тест connect-domain без выстреливания ошибки
var
        connect = require('connect'),
        connectDomain = require('connect-domain');

var app = connect()
        .use(connectDomain())
        .use(function(req, res){
                process.nextTick(function() {
                        res.end('Hello from Connect!');
                });
        })
        .use(function(err, req, res, next) {
                res.end(err.message);
        });

app.listen(3131);



Тест connect-domain с выстреливанием ошибки
var
        connect = require('connect'),
        connectDomain = require('connect-domain');

var app = connect()
        .use(connectDomain())
        .use(function(req, res){
                process.nextTick(function() {
                        if (Math.random() > 0.5) {
                                throw new Error('Asynchronous error from process.nextTick');
                        } else {
                                res.end('Hello from Connect!');
                        }
                });
        })
        .use(function(err, req, res, next) {
                res.end(err.message);
        });

app.listen(3131);



Тест trycatch без выстреливания ошибки
var
        connect = require('connect'),
        trycatch = require('trycatch');

var app = connect()
        .use(function (req, res, next) {
                trycatch(function () {
                        next();
                }, function (err) {
                        next(err);
                });
        })
        .use(function (req, res){
                process.nextTick(function () {
                        res.end('Hello from Connect!');
                });
        })
        .use(function (err, req, res, next) {
                res.end(err.message);
        });

app.listen(3131);



Тест trycatch с выстреливанием ошибки
var
        connect = require('connect'),
        trycatch = require('trycatch');

var app = connect()
        .use(function (req, res, next) {
                trycatch(function () {
                        next();
                }, function (err) {
                        next(err);
                });
        })
        .use(function (req, res){
                process.nextTick(function () {
                        if (Math.random() > 0.5) {
                                throw new Error('Asynchronous error from process.nextTick');
                        } else {
                                res.end('Hello from Connect!');
                        }
                });
        })
        .use(function (err, req, res, next) {
                res.end(err.message);
        });

app.listen(3131);



Тестировал утилитой ab.
Да, возможно, домены — не такая уж и плохая штука. Посмотрю на них повнимательнее на досуге.
Ваши результаты воспроизводятся. У меня trycatch тоже медленнее, но надо понимать, что разница в 20-30% на hello world — выльются в какие-нибудь 5-10% на реальном приложении, а это уже не так уж и много.
Sign up to leave a comment.

Articles