23. Уроки Node.js. Домены, “асинхронный try..catch”. Часть 3.

23_3
Итак, из чего состоит app.js?

Он состоит из того, что мы с самого начала делаем  домен, и запускаем в нем все наше приложение. Здесь подключаются все модули, создается сервер и т.д. Почему  мы подключаем модули здесь? Потому что во время подключения модулей могут подключаться какие-то другие модули, а те могут подключать – третьи, и т.д.

Многие из этих модулей я вообще не знаю. Они используются в каких-то библиотеках. При подключении модулей и их инициализации могут создавать какие-то объекты – EventEmitters. Я хочу быть уверенным, что эти EventEmitter подключаться именно к домену server.domain. Но основная задача этого кода – это запустить http.server:


В обработчике запроса мы тоже создаем новый домен:


Этот домен будет заниматься обработкой ошибок, которые появились во время данного конкретного запроса. Его преимущество – это то, что он знает про запрос. У него есть обработчик on.error, который, если какая-то ошибка, может ответить: 500, извините, ошибка. И не только ответить, он может  записать вlog именно то, что такой запрос пришел и такая-то ошибка. Таким образом, вы, как разработчик, будете видеть, что у Вас был такой запрос, и мне будет гораздо проще эту ошибку воспроизвести и исправить.


Мы привязываем к домену объекты req и res, поскольку они, во-первых, являются EventEmitters, во-вторых, они были созданы во внешнем коде Node.js. Поэтому мы вынуждены привязать их явно.

Допустим, выполняется handler. Произошла ошибка. Мы ответили человеку: “до свидания, извини, тут проблема” . Записали все, что надо в log. У нас один сервер поддерживает множество клиентов. Если те клиенты общались как-то между собой, то в произвольном месте выскочившая ошибка, которая может быть достаточно серьезной  – создаст кучу проблем. Обратите внимание, домены какие попало ошибки здесь у нас не ловят,

Обычно ошибки мы перехватываем в callback, try catch, а это именно какая-то программная ошибка, скорее всего. Соответственно, ее появление означает, что сервер сейчас находится в непонятном состоянии, и оставлять его в нем достаточно опасно. Поэтому хорошо бы этот сервер прибить и поднять новый чистый процесс Node.js.  Есть, конечно, ситуации, в которых можно оставить сервер работать дальше, но давайте делать безопаснее. Для этого  вызываем


таким образом  передаем работу с ошибкой on.error. Обращаем ваше внимание еще на такой нюанс, домены, конечно, похожи на try catch, но это все-таки не они. Если в этом on.error сделать throw, то управление в serverDomain.on не попадет.  Вместо этого такой throw отовсюду выпадет и повалит процесс.


Поэтому вызываем  внешний домен именно через Emit . Дальше обработчик на внешнем домене будет заниматься тем, что завершит обработку ошибки и, по возможности, мягко загасит текущий процесс сервера. На код ниже внимание можете не обращать:


Он схематичный.  Нам здесь важны именно домены. Мы получили рабочую схему для вложенной обработки ошибок. По возможности мы стараемся обработать ошибки в контексте запроса:


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

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

Возможно ли такое, что во время обработки запроса ниже, ошибка, которая произойдет, выскочит за пределы reqDomainв app.js?


Ответ: да, возможно. Чтобы это продемонстрировать,  немного модифицируем пример, этот пример справедлив для старых версий Node.js, в сегодняшних версиях (а это 7.1) таких проблем нет, также как и самого модуля Domain, т.к. он устарел (пример может понадобиться для понимания Legacy Code):


В handler.js у нас есть база данных, это может быть MongoDB, MySQL, Redis (необходимо иметь установленный Redis у себя на компьютере и клиент для него, установите последний с помощью:

npm install redis 

сама база устанавливается отдельно).

у них у всех одна и та же проблема с  при работе с доменами.


Для того, чтобы ответить посетителю, нужно сделать запрос к базе. Дальше мы должны что-то сделать с данными, возможно, обработать или могут понадобиться другие запросы.  Так или иначе в процессе произошла какая-то ошибка. Куда же она попадет? Запускаем и переходим на страницу:

127.0.0.1:3000

Видим, что соединение было разорвано. Server error – это то, что выводит домен ниже:


То есть, ошибка throw new Error ("redis callback") вовсе не была перехвачена обработчиком:


она пошла сразу в верхнюю часть. Почему? Ведь ошибка возникла в процессе работы обработчика function handler(req, res) , как так получилось, что она вышла из requestDomain ?

Для того, чтобы ответить на этот вопрос необходимо вспомнить, что существуют всего 2 способа, как домен переходит в callback асинхронных вызовов. Первый – это внутренний метод Node.js: встроенный setTimeout, fs.readFile и т.д. Они интегрированы с доменами и передают их дальше в callback. Второй – это всякие  EventEmitter. Если бы EventEmitter  был создан в контексте домена, то он к нему привязывается.

Метод redis.get, который мы здесь вызываем, использует объект redis, который был создан вне контекста запроса, а при подключении модуля. В Node.js это стандартная практика, одно соединение используется для множества запросов. Соответственно, если мы хотим получить какие-то данные, то мы даем объекту redis то, что мы хотим получить (data), и какой-то callback. Дальше этот объект что-то делает, что-то отсылает в базу данных, и у него есть внутренние EventEmitter, которые были созданы вместе с ним, и которые будут генерировать события, когда база что-то ответит. И эти внутренние EventEmitter при генерации события передадут домен. Какой? Конечно же, тот, с которым они были созданы, то внешний домен из app.js :


Получится, что этот callback будет вызван в домене serverDomain и не сохранит контекста запроса. Дальше, если этот callback вызвал какие-то другие функции, то, соответственно, мы получим тот же домен, и requestDomain для нас будет потерян.

Что делать? Есть 2 варианта. Первый – это поставить специальный вызов process.domain.bind.


Этот вызов возвращает обертку вокруг функции, которая привязывает его к текущему активному домену, то есть, домену запроса. Если мы сейчас запустим наш сервер, то все будет хорошо. (В текущей версии Node.js – 7.1 все сработает и без этой записи, но для старых версий можно встретить и такую запись).

Все, что надо, сработало. Вы должны увидеть в браузере:
Sorry, Error: redis callback;

Код урока вы сможете найти здесь|

23_end

Материалы для статьи взяты из следующего скринкаста

We are looking forward to meeting you on our website soshace.com

About the author

Stay Informed

It's important to keep up
with industry - subscribe!

Stay Informed

Looks good!
Please enter the correct name.
Please enter the correct email.
Looks good!

Related articles

Уроки Express.js . Логгер, Конфигурация, Шаблонизация с EJS. Часть 2.

Favicon – это все connect Middleware, он смотрит, если url имеет вид favicon.ico, то он читает favicon и ...

3. Уроки Express.js. Шаблонизация с EJS: Layout, Block, Partials

В реальной жизни у нас обычно больше, чем один шаблон. Более того, если уж так ...

24.11.2016

Уроки Express.js. Основы и Middleware. Часть 2.

Всем привет! Давайте продолжим наш урок об основах Express и Middleware. Итог (добавим в ...

No comments yet

Sign in

Forgot password?

Or use a social network account

 

By Signing In \ Signing Up, you agree to our privacy policy

Password recovery

You can also try to

Or use a social network account

 

By Signing In \ Signing Up, you agree to our privacy policy