#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. уродливое, но функциональное