Coding With Fun
Home Docker Django Node.js Articles Python pip guide FAQ Policy

Node .js domain


May 10, 2021 Node.js


Table of contents


Domain

稳定性: 2 - 不稳定

Node.js domain contains methods that can think of different IO operations as separate groups. If any of the events registered to error event, or throw an exception, the domain receives a notification instead of losing the context of the error in the process.on('uncaughtException') does not immediately exit the program with an error code.

Warning: Don't ignore mistakes!

You can't think of a domain error handler as an alternative to shutting down a process when an error occurs.

According to how thrown exceptions in JavaScript work, there is basically no way to safely "go back to where you left off" without revealing references or causing some other undefined state.

The safest way to respond to a throw error is to shut down the process. A normal server can have many active connections because it is obviously unreasonable for an error to close all connections.

A better approach is to send an error response to the request that triggered the error, and stop listening to the new request from the person who triggered the error while the other connections are working properly.

In this way, domain and cluster modules can work together, and when a process encounters an error, the main process can replicate a new process. For Node programs, terminal agents, or registered services, you can be aware of errors and react.

For example, the following code is not a good idea:

javascript
// XXX WARNING!  BAD IDEA!

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);
});

By using the context of the domain and cutting the program into multiple working processes, we can respond more rationally and handle errors more securely:

javascript
// 好一些的做法!

var cluster = require('cluster');
var 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', function(worker) {
    console.error('disconnect!');
    cluster.fork();
  });

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

  var domain = require('domain');

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

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

// This part isn't 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(function() {
        // Whoops!
        flerb.bark();
      });
      break;
    default:
      res.end('ok');
  }
}

Additional content for the wrong object

Whenever an error is routed to a domain, several fields are added.

  • error.domain the first domain to handle errors
  • error.domainEmitter this error object to trigger the event distributor for the 'error' event
  • error.domainBound bound to domainain's callback function, and the first argument is error.
  • error.domainThrown boolean value, indicating that the return function is thrown, distributed, or passed to the binding.

Implicit binding

Newly distributed objects, including Stream objects, requests, responses, and so on, are implicitly bound to the domain currently in use.

In addition, callback functions passed to the underlying event loop, such as fs.open or other methods for receiving callbacks, are automatically bound to the domain. If they throw an exception, the domain catches the error message.

To avoid overuse of memory, domain objects are not implicitly added as child objects of a valid domain. If you do this, it can easily affect garbage collection of request and response objects.

If you want to embed domain objects as child objects into the parent domain, you must explicitly add them.

Implicitly binding routes throws 'error' but does not register the event distributor to the domain.dispose() does not close the event distributor. I mplicit binding only needs to be aware of thrown errors 'error' events.

Explicit binding

Sometimes the domain being used is not the domain of an event distributor. Or, the event distributor might be created in one domain, but bound to another.

For example, an HTTP server uses a positive domain object, but we want to be able to use a different domain for each request.

This can be done by explicit binding.

For example:

// create a top-level domain for the server
var serverDomain = domain.create();

serverDomain.run(function() {
  // server is created in the scope of serverDomain
  http.createServer(function(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.
    var reqd = domain.create();
    reqd.add(req);
    reqd.add(res);
    reqd.on('error', function(er) {
      console.error('Error', er, req.url);
      try {
        res.writeHead(500);
        res.end('Error occurred, sorry.');
      } catch (er) {
        console.error('Error sending 500', er, req.url);
      }
    });
  }).listen(1337);
});

domain.create()

  • return: {Domain}

Used to return a new domain object.

Class: Domain

This class encapsulates the ability to bring errors and unscathed exceptions to valid objects.

The domain is a sub-class of EventEmitter. L isten for its error the caught error.

domain.run(fn)

  • fn {Function}

Running the provided function in the context of the domain implicitly binds all event distributors, timers, and underlying requests.

This is the basic way to use domains.

For example:

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

In this example, the program does not d.on('error')

domain.members

  • {Array}

An array of timers and event distributors that are explicitly added to the domain.

domain.add(emitter)

  • emitter {EventEmitter | Timer's timers and event distributors that are added to the domain

Explicitly add a distributor to the domain. I f the event handler called by the distributor throws an error, or the error it will direct error event, just like implicit binding.

The setInterval setTimeout If these callback functions throw an error, they will be snapped by the domain's 'error' processor.

If the timer or distributor is already bound to the domain, it will be removed from the last domain and bound to the current domain.

domain.remove(emitter)

  • emitter {EventEmitter | The distributor or timer to remove

In contrast to the domain.add function, which removes the distributor from the domain.

domain.bind(callback)

  • callback callback function
  • Return: The function that is bound by the function

The returned function is a wrapper function for the provided callback function. When this returned function is called, all thrown errors are directed to the domain's error event.

Example

var d = domain.create();

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

d.on('error', function(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 callback function
  • Return: The function that was intercepted by the function

Similar domain.bind(callback) I n addition to catching the thrown error, it also intercepts the Error object from being passed to the function as an argument.

In this way, the if (er) return callback(er); Patterns can be replaced by an error handling in one place.

Example

var d = domain.create();

function readSomeFile(filename, cb) {
  fs.readFile(filename, 'utf8', d.intercept(function(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', function(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()

This function is like run system bind and intercept which sets a valid domain. I t sets the domain.active process.domain implicitly pushes the domain to the domain stack managed by the domain module domain.exit() The call of the enter function separates the asynchronous call chain from the end or interruption of the I/O operation bound to a domain.

Calling enter only the active domain, not the domain itself. Enter and exit can be called as many times as Enter a separate exit

domain.exit()

exit function exits the current domain and is removed from the domain's stack. W henever the program's execution process is to switch to a different asynchronous call chain, make sure to exit the current domain. Call the exit function, separate the asynchronous call chain, and bind to the end or break of an I/O operation bound to a domain.

If more than one nested domain is bound to the current context, exit function exits all nesting.

Calling exit only the active domain, not its own. Enter and exit can be called as many times as Enter a separate exit

If the exit is already set under this domain name, exit will not exit the domain return.

domain.dispose()

稳定性: 0 - 抛弃。通过域里设置的错误事件来显示的消除失败的 IO 操作。

After dispos is called, callback functions bound to the domain through run, bind, or intercept no longer use the domain and distribute the dispose event.