Восстановление из неперехваченного исключения в Node.JS

#exception #node.js #express #uncaught-exception

#исключение #node.js #экспресс #неперехваченное исключение

Вопрос:

Хорошо, итак, у меня проблема. Если неперехваченное исключение возникает во время обработки HTTP-запроса, у меня нет возможности вызвать метод end() по http.Объект ServerResponse. Таким образом, сервер зависает навсегда и никогда не выполняет запрос.

Вот пример:

 var express = require('express');
var app = express.createServer();
var reqNum = 0;
app.get('/favicon.ico', function(req, res) {res.send(404);});
app.get('*', function(req, res, next) {
    console.log("Request #",   reqNum, ":", req.url);
    next();
});
app.get('/error', function(req, res, next) {
    throw new Error("Problem occurred");
});
app.get('/hang', function(req, res, next) {
    console.log("In /hang route");
    setTimeout(function() {
        console.log("In /hang callback");
        if(reqNum >= 3)
            throw new Error("Problem occurred");
        res.send("It worked!");
    }, 2000);
});
process.on('uncaughtException', function(err) {
    console.log("Uncaught exception!", err);
});
app.listen(8080);
  

При посещении /error возникает исключение, но оно перехватывается. Пользователь получает сообщение об ошибке — никаких проблем. Однако, если я посещу / зависну, сервер в конечном итоге выдаст неперехваченное исключение и зависнет навсегда. Все последующие запросы на / hang будут зависать.

Это отстой. Есть какие-либо рекомендации по устранению этой проблемы?

Ответ №1:

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

Комментарии:

1. В определенной степени я согласен. В производственных средах вы не можете отключить весь сервер только из-за одного неперехваченного исключения. Что, если проблема возникла на одной странице PHP? Выйдет из строя весь ваш веб-сервер? Я собирался отправить серверу электронное письмо, если возникнет неперехваченное исключение, и автоматически завершить работу, если в течение 60 секунд возникнет более 10. Но, на самом деле… Я не хочу, чтобы сервер выходил из строя и перезагружался. Я хотел бы попытаться восстановить.

2. @BMiner Перезапустить ваше приложение проще, чем извиниться перед клиентом за повреждение их данных. Как сказал thejh, среда выполнения находится в нерабочем состоянии. С этого момента вы должны предполагать, что все является неопределенным поведением.

3. @thejh Почему я должен предполагать, что нахожусь в нечистом состоянии? Возможно, повреждена только часть программы? Почему безопаснее просто завершить работу и перезапустить? Я верю вам, ребята, но я все еще настроен скептически. Не могли бы вы привести пример?

4. @BMiner: Ты говоришь «возможно». Дело в том, что throw похоже на GOTO, оно выскакивает из вашего кода, когда работа выполнена наполовину, и теперь ваше состояние может быть повреждено. Пример: user.name = newname; db.save(user.id, 'name', newname) — что, если db.save произойдет сбой до его завершения? Ваша база данных содержит имя, отличное от того, которое у вас есть в оперативной памяти.

5. Полностью согласен с @thejh, вам следует немедленно завершить процесс (во избежание утечки данных и т.д.), А затем перезапустить ваш сервер.

Ответ №2:

Если ошибка выдается синхронно, express не перестанет работать, а только вернет 500.

 this.app.get("/error", (request, response) => {
  throw new Error("shouldn't stop");
});
  

Если ошибка выдается асинхронно, произойдет сбой express. Но, согласно официальной документации, все еще существует способ восстановления после него, вызвав next :

 this.app.get("/error", (request, response, next) => {
  setTimeout(() => {
    try {
      throw new Error("shouldn't stop");
    } catch (err) {
      next(err);
    }
  }, 0);
});
  

Это позволит express выполнить свои обязанности по ответу с ошибкой 500.

Ответ №3:

Используйте try/catch/finally.

 app.get('/hang', function(req, res, next) {
    console.log("In /hang route");
    setTimeout(function() {
        console.log("In /hang callback");
        try {
            if(reqNum >= 3)
                throw new Error("Problem occurred");
        } catch (err) {
            console.log("There was an error", err);
        } finally {
            res.send("It worked!");
        }
    }, 2000);
});
  

Комментарии:

1. уродливое, но функциональное