16Nov

node23_2

Продолжаем наш урок. Давайте немного усложним этот пример, добавив работу с файлами вот таким образом:

var domain = require('domain');
var fs = require('fs');
var d = domain.create(), server;

d.on('error', function(err) {
    console.error("Domain has caught %s", err);
});


d.run(function() {

    setTimeout(function() {
      fs.readFile(__filename, function() {
          ERROR();
      });
        }, 1000);

});

Запускаем. Как ни странно, оно все еще работает. Почему? Как домен из данной функции перекочевал в fs.readFile(__filename, function()? Да все оттуда же. Потому что внутренняя реализация функции readFile знает про домены. Когда она запускает callback, то она запускает его в контексте того же домена. Благодаря этому все хорошо.

Рассмотрим последний пример.  Создаем новый объект:

server = new http.Server();

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

server.on('boom', function() {
    setTimeout(function() {
        fs.readFile(__filename, function() {
            ERROR();
        });
    }, 1000);
});

И для того, чтобы еще больше все запутать, вынесем этот код вниз. То есть, мы создаем объект в одном месте, а ошибка будет вообще после того, как запуск в домене закончился. Обработает ли ее домен? Запускаем наш код, полностью он выглядит вот так:

var domain = require('domain');
var fs = require('fs'), http = require('http');

var d = domain.create(), server;

d.on('error', function(err) {
    console.error("Domain has caught %s", err);
});

d.run(function() {

    server = new http.Server();

});

server.on('boom', function() {
    setTimeout(function() {
        fs.readFile(__filename, function() {
            ERROR();
        });
    }, 1000);
});

server.emit('boom');

Как видим, все отлично обработалось. Каким образом эта функция узнала про домен? Все благодаря интеграции. Ведь сервер – это EventEmitter. Этот модуль знает про домены, и, когда создается любой EventEmitter, если есть текущий активный домен, то он получает ссылку на него, где ее можно вывести. добавим console.log:

server = new http.Server();  
console.log(server.domain); 

Запускаем. Все, есть. После того, как EventEmitter привязан к домену, любые обработчики он запускает именно в контексте этого домена. Конечно же, если сервер создается вне активного домена, то ни о каком server.domain не может идти и речи. Впрочем, с такими объектами тоже можно иметь дело, необходимо лишь добавить их домен вручную: специальный вызов add:

server = new http.Server();

d.run(function() {

    d.add(server);

    console.log(server.domain);

});

Если мы сейчас это вызовем, то все будет хорошо, ошибка перехвачена. Единственная тонкость, которую здесь надо иметь в  виду, это управление памятью. Дело в том, что если EventEmitter создан именно в контексте домена, то он получает ссылку, в данном случае server.domain. А если он был создан ранее и добавлен через add, то он получает не только сам ссылку на этот домен, но еще и домен ссылается на него. То есть, у домена есть специальный массив members, где он ссылается на все, что ссылается через  add. Во всяком случае, такова текущая реализация. Соответственно, получается, что с add мы имеем двустороннюю ссылку serverdomain. В результате получается, что память может быть очищена не от сервера в отдельности, не от домена в отдельности, а от них обоих вместе. То есть, если есть какой-то долго живущий домен, и к нему через add  прибавляется много всего, то память не будет очищена, пока домен не умрет, либо пока когда это станет возможным, не будет вызов d.remove(server). Как правило, в скриптах эти штуки используют редко, обычно стараются создавать все, что нужно, внутри домена, и таких проблем не возникает.

Вернемся к примеру, который рассматривали в первой части статьи, то есть, к серверу. Перейдите на commit под названием domain_1-11:

Как вы считаете, в чем же дело? Почему ошибка в обработчике запроса вылетела и не была обработана доменом? Как это поправить? Дело в том, что сервер создан вне домена. Таким образом, handler это обработчик события request. Так как сервер создан вне домена, то при вызове обработчика никакой домен ему не передается, и throw, как и раньше, валит весь процесс. Чтобы все было хорошо, достаточно взять этот объект server и создать его внутри вызова run:

serverDomain.run(function() {  
  var server = require('./server');  
  server.listen(3000);  
});

Вот так будет все нормально, поскольку сервер привязан к домену. Проверим. Запускаем код. Вызываем Chrome и переходим по тому же url:

http://127.0.0.1:3000/

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

А как нам ответить? Здесьserver.domain перехватил ошибку, но никакой информации о том, где она, у него нет. Для того, чтобы ее получить, мы будем создавать домен отдельно для каждого запроса. Выглядит это следующим образом. Есть два файла. Первый  app.js – главный, основной файл приложения:

var domain = require('domain');
var serverDomain = domain.create();

var server;

serverDomain.on('error', function(err) {
   console.error("Server error", err);
   if (server) server.close();

   setTimeout(function () {
       process.exit(1);
   }, 1000).unref();
});


serverDomain.run(function() {
    var http = require('http');
    var handler = require('./handler');
    //var database = require ('mongodb');

     server = http.createServer (function(req, res) {

       var reqDomain = domain.create();
        reqDomain.add(req);
        reqDomain.add(res);

        reqDomain.on('error', function(err) {
          res.statusCode = 500;
          res.end("Sorry, " + err);
           // ...
          serverDomain.emit('error', err);
        });

        reqDomain.run(function() {
           handler(req, res);
         });
       });

     server.listen(3000);
});

Он занимается тем, что запускает сервер, создает домены. Второй – handler.js, модуль, который занимается обработкой запросов:

var fs = require('fs');

module.exports = function handler(req, res) {
    if (req.url == '/') {

        fs.readFile('no-such-file', function(err, content) {

            if (err) throw err; // JSON.parse("invalid!")

            res.end(content);
        });

    } else {
        res.statusCode = 404;
        res.end("Not Found");
    }

};

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

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

to-be-continued-series-8

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

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

Уроки React. Урок 13. Часть 2.

“Уважаемы читатели! Не забудьте про первую часть этой статьи, новичкам стоит начать с Урока 1.”

Теперь давайте это вызовем в containers/Articles.js , добавив такую запись вызывая action creater:

19. Уроки Node.js. Безопасный Путь к Файлу в fs и path.

В этой статье мы рассмотрим, как при помощи Node.js создать веб-сервер, который будет возвращать файл пользователю из директории public. Может возникнуть вопрос: зачем здесь Node.js? почему бы не сделать это на другом сервере? Вопрос совершенно уместен. Да, для отдачи файлов, как правило, другие сервера будут более эффективны. С другой стороны, Node.js, во-первых, тоже работает весьма неплохо, а во-вторых, перед отдачей файла может совершить какие-то интеллектуальные действия, например, обратиться к базе данных, проверить, имеет ли пользователь право на доступ к данному файлу, и только если имеет, тогда уже отдавать.

Уроки React. Урок 5.

Для того чтобы проверить нашу структуру следует пользоваться React dev tools. Скачать их можно здесь. Позволяет нам наблюдать всю структуру Вашего Virual DOM, Ваших компонентов и сторонние компоненты. Также здесь есть доступ к данным, можно посмотреть их Props,State и.т.п. Ставиться оно как расширение для вашего браузера и будет доступна при вызове инструментов разработчика. Для начала работы с React либо с новым проектом это очень полезно. Здесь видны названия ваших компонентов, например OneOpen, ArticleList, CommentList и др.
Допустим если не называть class OneOpen то в DevTools будет непонятно какой код отвечает за работу компонента к примеру, а вот называя его вы облегчаете себе дальнейший Debug.

Leave a Reply