Analyze the usage of domain module in Node. js exception handling

  • 2020-12-21 17:57:03
  • OfStack

NodeJS provides the domain module to simplify exception handling for asynchronous code. Before we introduce this module, we need to understand the concept of a domain. Simply put, a domain is an JS runtime where an exception is thrown as a global exception if it is not caught. NodeJS provides a way to catch global exceptions through the process object, as shown in the sample code below


process.on('uncaughtException', function (err) {
  console.log('Error: %s', err.message);
});

setTimeout(function (fn) {
  fn();
});


Error: undefined is not a function

Although there is a place for global exceptions to be caught, for the most part, we want to catch them as early as possible and determine the execution path of the code based on the results. We use the following HTTP server code as an example:


function async(request, callback) {
  // Do something.
  asyncA(request, function (err, data) {
    if (err) {
      callback(err);
    } else {
      // Do something
      asyncB(request, function (err, data) {
        if (err) {
          callback(err);
        } else {
          // Do something
          asyncC(request, function (err, data) {
            if (err) {
              callback(err);
            } else {
              // Do something
              callback(null, data);
            }
          });
        }
      });
    }
  });
}

http.createServer(function (request, response) {
  async(request, function (err, data) {
    if (err) {
      response.writeHead(500);
      response.end();
    } else {
      response.writeHead(200);
      response.end(data);
    }
  });
});

The above code hands the request object over to the asynchronous function and then returns the response based on the processing results. A callback function is used to pass exceptions, so a few more asynchronous function calls inside the async function would make the code look like this. To make the code look nice, we can use the domain module to create a subdomain (JS subruntime) for each request we process. Code running within a subdomain can throw exceptions at will, and these exceptions can be caught by error event series 1 of the subdomain object. Therefore, the above code can be modified as follows:


function async(request, callback) {
  // Do something.
  asyncA(request, function (data) {
    // Do something
    asyncB(request, function (data) {
      // Do something
      asyncC(request, function (data) {
        // Do something
        callback(data);
      });
    });
  });
}

http.createServer(function (request, response) {
  var d = domain.create();

  d.on('error', function () {
    response.writeHead(500);
    response.end();
  });

  d.run(function () {
    async(request, function (data) {
      response.writeHead(200);
      response.end(data);
    });
  });
});

As you can see, we have created a subdomain object using the.create method and have entered the entry point of the code that needs to run in the subdomain through the.run method. The asynchronous function callback function in the subdomain no longer needs to catch exceptions, so code 1 is much slimmer.

trap
Whether a global exception is caught through the uncaughtException event of the process object or a subdomain exception is caught through the error event of the subdomain object, it is strongly recommended in the NodeJS official documentation to restart the program immediately after handling the exception rather than letting the program continue running. According to the official documentation, the program is in an indefinable state of operation after an exception. If it does not exit immediately, the program can have a serious memory leak or behave strangely.

But there are a few facts to clarify here. JS itself throw.. try.. The catch exception handling mechanism does not cause memory leaks or unexpected results, but NodeJS is not the quintessential JS. A large number of API in NodeJS is implemented by C/C++, so during the running process of NodeJS program, the code execution path shuttles inside and outside the JS engine, and the exception throwing mechanism of JS may interrupt the normal code execution process, leading to abnormal performance of the C/C++ part of the code, and then lead to memory leak and other problems.

Therefore, when using uncaughtException or domain to catch exceptions, if the code execution path involves the C/C++ part of the code, it is better to restart the program after handling the exception if you are not sure whether it will cause a memory leak or other problems. When using try statement to catch exceptions, 1 usually catches exceptions of JS itself, so there is no need to worry about appeals.


Related articles: