Spec-Zone.ru › Node.js 6 LTS

Домен

Устойчивость: 0 - Устаревшее

Этот модуль ожидает устаревания. После завершения разработки замены API этот модуль будет полностью устаревшим. Большинство конечных пользователей не должны использовать этот модуль. Пользователи, которым абсолютно необходима функциональность доменов, могут полагаться на неё на данный момент, но должны ожидать необходимости миграции на другое решение в будущем.

Домены предоставляют способ обработки нескольких различных операций ввода-вывода как единой группы. Если любой из эмиттеров событий или обратных вызовов, зарегистрированных в домене, генерирует событие 'error' или вызывает ошибку, то объект домена будет уведомлен, вместо того чтобы потерять контекст ошибки в обработчике process.on('uncaughtException') или привести к немедленному завершению программы с кодом ошибки.

Предупреждение: Не игнорируйте ошибки!

Обработчики ошибок домена не являются заменой закрытию вашего процесса при возникновении ошибки.

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

Самый безопасный способ реагировать на брошенную ошибку — это закрыть процесс. Конечно, в обычном веб-сервере у вас может быть много открытых подключений, и неразумно резко закрывать их, потому что ошибка была вызвана кем-то другим.

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

Таким образом, использование domain тесно связано с модулем кластеризации, поскольку мастер-процесс может создать новый процесс при обнаружении ошибки в рабочем процессе. Для программ Node.js, масштабируемых на нескольких машинах, прокси-сервер завершения или реестр служб могут заметить ошибку и отреагировать соответствующим образом.

Например, это плохая идея:

// XXX WARNING!  BAD IDEA!

const d = require('domain').create();
d.on('error', (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(() => {
  require('http').createServer((req, res) => {
    handleRequest(req, res);
  }).listen(PORT);
});

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

// Much better!

const cluster = require('cluster');
const PORT = +process.env.PORT || 1337;

if (cluster.isMaster) {
  // In real life, you'd probably use more than just 2 workers,
  // and perhaps not put the master and worker in the same file.
  //
  // You can also of course get a bit fancier about logging, and
  // implement whatever custom logic you need to prevent DoS
  // attacks and other bad behavior.
  //
  // See the options in the cluster documentation.
  //
  // The important thing is that the master does very little,
  // increasing our resilience to unexpected errors.

  cluster.fork();
  cluster.fork();

  cluster.on('disconnect', (worker) => {
    console.error('disconnect!');
    cluster.fork();
  });

} else {
  // the worker
  //
  // This is where we put our bugs!

  const domain = require('domain');

  // See the cluster documentation for more details about using
  // worker processes to serve requests. How it works, caveats, etc.

  const server = require('http').createServer((req, res) => {
    const d = domain.create();
    d.on('error', (er) => {
      console.error(`error ${er.stack}`);

      // Note: we're in dangerous territory!
      // By definition, something unexpected occurred,
      // which we probably didn't want.
      // Anything can happen now!  Be very careful!

      try {
        // make sure we close down within 30 seconds
        const killtimer = setTimeout(() => {
          process.exit(1);
        }, 30000);
        // But don't keep the process open just for that!
        killtimer.unref();

        // stop taking new requests.
        server.close();

        // Let the master know we're dead. This will trigger a
        // 'disconnect' in the cluster master, and then it will fork
        // a new worker.
        cluster.worker.disconnect();

        // try to send an error to the request that triggered the problem
        res.statusCode = 500;
        res.setHeader('content-type', 'text/plain');
        res.end('Oops, there was a problem!\n');
      } catch (er2) {
        // oh well, not much we can do at this point.
        console.error(`Error sending 500! ${er2.stack}`);
      }
    });

    // Because req and res were created before this domain existed,
    // we need to explicitly add them.
    // See the explanation of implicit vs explicit binding below.
    d.add(req);
    d.add(res);

    // Now run the handler function in the domain.
    d.run(() => {
      handleRequest(req, res);
    });
  });
  server.listen(PORT);
}

// This part is not important. Just an example routing thing.
// You'd put your fancy application logic here.
function handleRequest(req, res) {
  switch (req.url) {
    case '/error':
      // We do some async stuff, and then...
      setTimeout(() => {
        // Whoops!
        flerb.bark();
      }, timeout);
      break;
    default:
      res.end('ok');
  }
}

Добавления в объекты ошибок

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

  • error.domain Домен, который первым обработал ошибку.
  • error.domainEmitter Эмиттер событий, который генерировал событие 'error' с объектом ошибки.
  • error.domainBound Функция обратного вызова, которая была связана с доменом и получила ошибку в качестве первого аргумента.
  • error.domainThrown Булево значение, указывающее, была ли ошибка брошена, генерирована или передана функции обратного вызова, связанной с доменом.

Неявное связывание

Если используются домены, то все **новые** объекты EventEmitter (включая объекты Stream, запросы, ответы и т. д.) будут неявно связаны с активным доменом в момент их создания.

Кроме того, обратные вызовы, передаваемые в запросы низкоуровневого цикла событий (например, в fs.open или другие методы, принимающие обратные вызовы), будут автоматически связаны с активным доменом. Если они выбросят ошибку, домен перехватит её.

Чтобы предотвратить чрезмерное использование памяти, сами объекты Domain не добавляются неявно в качестве дочерних элементов активного домена. Если бы это было так, то было бы слишком легко предотвратить надлежащее удаление объектов запросов и ответов.

Если вы хотите вложить объекты Domain в качестве дочерних элементов родительского домена, то должны добавить их явно.

Неявное связывание перенаправляет брошенные ошибки и события 'error' в событие 'error' домена, но не регистрирует EventEmitter в домене, поэтому domain.dispose() не завершит EventEmitter. Неявное связывание обрабатывает только брошенные ошибки и события 'error'.

Явное связывание

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

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

Это возможно благодаря явному связыванию.

Например:

// create a top-level domain for the server
const domain = require('domain');
const http = require('http');
const serverDomain = domain.create();

serverDomain.run(() => {
  // server is created in the scope of serverDomain
  http.createServer((req, res) => {
    // req and res are also created in the scope of serverDomain
    // however, we'd prefer to have a separate domain for each request.
    // create it first thing, and add req and res to it.
    const reqd = domain.create();
    reqd.add(req);
    reqd.add(res);
    reqd.on('error', (er) => {
      console.error('Error', er, req.url);
      try {
        res.writeHead(500);
        res.end('Error occurred, sorry.');
      } catch (er2) {
        console.error('Error sending 500', er2, req.url);
      }
    });
  }).listen(1337);
});

domain.create()

  • Возвращает: <Домен>

Возвращает новый объект Domain.

Класс: Домен

Класс Domain инкапсулирует функциональность перенаправления ошибок и непредвиденных исключений в активный объект Domain.

Domain — это дочерний класс EventEmitter. Для обработки перехваченных ошибок прослушивайте событие 'error'.

domain.run(fn[, ...args])

  • fn <Функция>
  • ...args <любое>

Выполняет предоставленную функцию в контексте домена, неявно связывая все эмиттеры событий, таймеры и запросы низкого уровня, которые создаются в этом контексте. Дополнительные аргументы могут быть переданы функции.

Это самый простой способ использования домена.

Пример:

const domain = require('domain');
const fs = require('fs');
const d = domain.create();
d.on('error', (er) => {
  console.error('Caught error!', er);
});
d.run(() => {
  process.nextTick(() => {
    setTimeout(() => { // simulating some various async stuff
      fs.open('non-existent file', 'r', (er, fd) => {
        if (er) throw er;
        // proceed...
      });
    }, 100);
  });
});

В этом примере будет сработать обработчик d.on('error'), а не аварийное завершение программы.

domain.members

  • <Массив>

Массив таймеров и эмиттеров событий, которые были явно добавлены в домен.

domain.add(emitter)

  • emitter <EventEmitter> | <Таймер> эмиттер или таймер для добавления в домен

Явно добавляет эмиттер в домен. Если обработчики событий, вызываемые эмиттером, вызывают ошибку или если эмиттер генерирует событие 'error', оно будет перенаправлено на событие 'error' домена, так же, как и при неявном связывании.

Это также работает с таймерами, возвращаемыми из setInterval() и setTimeout(). Если их функция обратного вызова вызывает ошибку, она будет перехвачена обработчиком ошибок домена.

Если таймер или EventEmitter уже были связаны с доменом, они удаляются оттуда и связываются с этим доменом вместо него.

domain.remove(emitter)

  • emitter <EventEmitter> | <Таймер> эмиттер или таймер для удаления из домена

Обратное domain.add(emitter). Удаляет обработку домена из указанного эмиттера.

domain.bind(callback)

  • callback <Функция> Функция обратного вызова
  • Возвращает: <Функция> Связанная функция

Возвращаемая функция будет обёрткой вокруг предоставленной функции обратного вызова. Когда вызывается возвращаемая функция, любые ошибки, которые возникают, будут перенаправлены на событие 'error' домена.

Пример

const d = domain.create();

function readSomeFile(filename, cb) {
  fs.readFile(filename, 'utf8', d.bind((er, data) => {
    // if this throws, it will also be passed to the domain
    return cb(er, data ? JSON.parse(data) : null);
  }));
}

d.on('error', (er) => {
  // an error occurred somewhere.
  // if we throw it now, it will crash the program
  // with the normal line number and stack message.
});

domain.intercept(callback)

  • callback <Функция> Функция обратного вызова
  • Возвращает: <Функция> Перехваченная функция

Этот метод почти идентичен domain.bind(callback). Однако помимо перехвата брошенных ошибок, он также перехватывает объекты Error, отправленные в качестве первого аргумента функции.

Таким образом, распространённый if (err) return callback(err); шаблон может быть заменён одним обработчиком ошибок в одном месте.

Пример

const d = domain.create();

function readSomeFile(filename, cb) {
  fs.readFile(filename, 'utf8', d.intercept((data) => {
    // note, the first argument is never passed to the
    // callback since it is assumed to be the 'Error' argument
    // and thus intercepted by the domain.

    // if this throws, it will also be passed to the domain
    // so the error-handling logic can be moved to the 'error'
    // event on the domain instead of being repeated throughout
    // the program.
    return cb(null, JSON.parse(data));
  }));
}

d.on('error', (er) => {
  // an error occurred somewhere.
  // if we throw it now, it will crash the program
  // with the normal line number and stack message.
});

domain.enter()

Метод enter используется модулем run, bind, и intercept для установки активного домена. Он устанавливает domain.active и process.domain в домен и неявно помещает домен в стек доменов, управляемый модулем домена (подробнее о стеке доменов см. domain.exit()). Вызов enter определяет начало цепочки асинхронных вызовов и операций ввода-вывода, связанных с доменом.

Вызов enter изменяет только активный домен и не изменяет сам домен. enter и exit могут вызываться произвольное число раз для одного домена.

Если домен, для которого вызывается enter, был удалён, enter вернётся без установки домена.

domain.exit()

Метод exit завершает текущий домен, извлекая его из стека доменов. Каждый раз, когда выполнение переключается в контекст другой цепочки асинхронных вызовов, важно убедиться, что текущий домен завершен. Вызов exit определяет конец или прерывание цепочки асинхронных вызовов и операций ввода-вывода, связанных с доменом.

Если существует несколько вложенных доменов, связанных с текущим контекстом выполнения, exit завершит все вложенные в этот домен домены.

Вызов exit изменяет только активный домен и не изменяет сам домен. enter и exit могут вызываться произвольное число раз для одного домена.

Если домен, для которого вызывается exit, был удалён, exit вернётся без завершения домена.

domain.dispose()

Устойчивость: 0 - Устаревший. Пожалуйста, явно восстанавливайтесь после неудачных операций ввода-вывода с помощью обработчиков событий об ошибках, установленных в домене.

После того, как dispose будет вызван, домен больше не будет использоваться обратными вызовами, привязанными к домену с помощью run, bind, или intercept, и событие 'dispose' будет выведено.

© Joyent, Inc. and other Node contributors
Licensed under the MIT License.
Node.js is a trademark of Joyent, Inc. and is used with its permission.
We are not endorsed by or affiliated with Joyent.
https://nodejs.org/dist/latest-v6.x/docs/api/domain.html

Spec-Zone.ru

Настройки Оффлайн Что нового Помощь О нас
Spec-Zone .ru
спецификации, руководства, описания, API