Подтвердить что ты не робот

Сделать node.js не выходить с ошибкой

Я работаю над ориентированным на websocket сервером node.js с помощью Socket.IO. Я заметил ошибку, когда некоторые браузеры не следуют правильной процедуре подключения к серверу, и код не написан, чтобы грамотно обрабатывать его, и, короче говоря, он вызывает метод для объекта, который никогда не был настроен, таким образом убивая сервер из-за ошибки.

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

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

4b9b3361

Ответ 1

Вы можете присоединить слушателя к событию uncaughtException объекта процесса.

Код, взятый из фактического Node.js Ссылка на API (это второй элемент под "process" ):

process.on('uncaughtException', function (err) {
  console.log('Caught exception: ', err);
});

setTimeout(function () {
  console.log('This will still run.');
}, 500);

// Intentionally cause an exception, but don't catch it.
nonexistentFunc();
console.log('This will not run.');

Все, что вам нужно сделать, это зарегистрировать его или сделать с ним что-либо, если вы знаете, при каких обстоятельствах возникает ошибка, вы должны подать сообщение об ошибке на странице Socket.IO GitHub:
https://github.com/LearnBoost/Socket.IO-node/issues

Ответ 2

Использование uncaughtException - очень плохая идея.

Лучшей альтернативой является использование доменов в Node.js 0.8. Если вы используете более раннюю версию Node.js, используйте forever, чтобы перезапустить свои процессы или даже лучше использовать node cluster, чтобы вызвать несколько рабочих процессов и перезапустить рабочего в событии uncaughtException.

От: http://nodejs.org/api/process.html#process_event_uncaughtexception

Предупреждение: правильное использование "uncaughtException"

Обратите внимание, что 'uncaughtException' является грубым механизмом обработки исключений, предназначенным для использования только в качестве последнего средства. Событие не должно использоваться в качестве эквивалента On Error Resume Next. Необработанные исключения по сути означают, что приложение находится в состоянии undefined. Попытка возобновить код приложения без надлежащего восстановления после исключения может вызвать дополнительные непредвиденные и непредсказуемые проблемы.

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

Попытка возобновить нормально после непоставленного исключения может быть похожа на вытаскивание шнура питания при обновлении компьютера - девять из десяти раз ничего не происходит - но в 10-й раз система становится поврежденной.

Правильное использование "uncaughtException" - это выполнить синхронную очистку выделенных ресурсов (например, дескрипторы файлов, дескрипторы и т.д.) перед закрытием процесса. Небезопасно возобновлять нормальную работу после "uncaughtException" .

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

Ответ 3

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

Некоторые фреймворки, такие как express, позволят вам поймать определенные типы ошибок (когда метод async возвращает объект ошибки), но есть и другие условия, которые вы не можете поймать с помощью глобального обработчика ошибок. Это ограничение (на мой взгляд) Node и, возможно, присуще асинхронному программированию в целом.

Например, скажем, у вас есть следующий экспресс-обработчик:

app.get("/test", function(req, res, next) {
    require("fs").readFile("/some/file", function(err, data) {
        if(err)
            next(err);
        else
            res.send("yay");
    });
});

Скажем, что файл "some/file" на самом деле не существует. В этом случае fs.readFile вернет ошибку в качестве первого аргумента методу обратного вызова. Если вы проверите это и сделаете следующее (ошибочно), когда это произойдет, обработчик ошибок по умолчанию будет захвачен и сделает все, что вы сделаете (например, верните 500 пользователю). Это грациозный способ обработки ошибки. Конечно, если вы забыли позвонить next(err), это не сработает.

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

app.get("/test", function(req, res, next) {
    require("fs").readFile("/some/file", function(err, data) {
        if(err)
            next(err);
        else {
            nullObject.someMethod(); //throws a null reference exception
            res.send("yay");
        }
    });
});

В этом случае возникает ошибка, если ваш код приводит к вызову метода для нулевого объекта. Здесь будет выведено исключение, оно не будет захвачено глобальным обработчиком ошибок, и ваше приложение Node завершится. Все клиенты, которые в настоящее время выполняют запросы на эту услугу, внезапно отключились без объяснения причин. Неловкий.

В настоящее время нет глобальных функций обработчика ошибок в Node для обработки этого случая. Вы не можете помещать гигантский try/catch вокруг всех ваших обработчиков, потому что к моменту выполнения вашего обратного вызова asyn эти блоки try/catch больше не находятся в области видимости. Это просто характер асинхронного кода, он разбивает парадигму обработки ошибок try/catch.

AFAIK, ваш единственный ресурс здесь состоит в том, чтобы поместить блоки try/catch вокруг синхронных частей вашего кода внутри каждого из ваших асинхронных обратных вызовов, примерно так:

app.get("/test", function(req, res, next) {
    require("fs").readFile("/some/file", function(err, data) {
        if(err) {
            next(err);
        }
        else {
            try {
                nullObject.someMethod(); //throws a null reference exception
                res.send("yay");
            }
            catch(e) {
                res.send(500);
            }
        }
    });
});

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

Некоторые люди думают, что то, что Node делает в этих случаях (то есть умереть), - это правильная вещь, потому что ваша система находится в противоречивом состоянии, и у вас нет другого выбора. Я не согласен с этими рассуждениями, но я не буду вдаваться в философские дебаты об этом. Дело в том, что с Node ваши варианты - это много маленьких блоков try/catch или надеюсь, что ваше тестовое покрытие будет достаточно хорошим, чтобы этого не произошло. Вы можете поместить что-то вроде upstart или supervisor, чтобы перезапустить приложение, когда оно идет вниз, но это просто смягчение проблемы, а не решение.

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

Ответ 4

Я только что собрал класс, который слушает необработанные исключения, и когда он видит его:

  • выводит трассировку стека на консоль.
  • записывает в него собственный файл журнала
  • отправляет вам трассировку стека
  • перезагружает сервер (или убивает его, зависит от вас)

Это потребует небольшой настройки для вашего приложения, поскольку я пока не сделал его общим, но это всего лишь несколько строк, и это может быть то, что вы ищете!

Проверьте это!

Примечание: на данный момент это более 4 лет, незаконченное, и теперь может быть лучший способ - я не знаю!)

process.on
(
    'uncaughtException',
    function (err)
    {
        var stack = err.stack;
        var timeout = 1;

        // print note to logger
        logger.log("SERVER CRASHED!");
        // logger.printLastLogs();
        logger.log(err, stack);


        // save log to timestamped logfile
        // var filename = "crash_" + _2.formatDate(new Date()) + ".log";
        // logger.log("LOGGING ERROR TO "+filename);
        // var fs = require('fs');
        // fs.writeFile('logs/'+filename, log);


        // email log to developer
        if(helper.Config.get('email_on_error') == 'true')
        {
            logger.log("EMAILING ERROR");
            require('./Mailer'); // this is a simple wrapper around nodemailer http://documentup.com/andris9/nodemailer/
            helper.Mailer.sendMail("GAMEHUB NODE SERVER CRASHED", stack);
            timeout = 10;
        }

        // Send signal to clients
//      logger.log("EMITTING SERVER DOWN CODE");
//      helper.IO.emit(SIGNALS.SERVER.DOWN, "The server has crashed unexpectedly. Restarting in 10s..");


        // If we exit straight away, the write log and send email operations wont have time to run
        setTimeout
        (
            function()
            {
                logger.log("KILLING PROCESS");
                process.exit();
            },
            // timeout * 1000
            timeout * 100000 // extra time. pm2 auto-restarts on crash...
        );
    }
);

Ответ 5

Была аналогичная проблема. Иво ответ хороший. Но как вы можете поймать ошибку в цикле и продолжить?

var folder='/anyFolder';
fs.readdir(folder, function(err,files){
    for(var i=0; i<files.length; i++){
        var stats = fs.statSync(folder+'/'+files[i]);
    }
});

Здесь fs.statSynch выдает ошибку (против скрытого файла в Windows, который barfs я не знаю почему). Ошибка может быть поймана методом process.on(...), но цикл останавливается.

Я попытался добавить обработчик напрямую:

var stats = fs.statSync(folder+'/'+files[i]).on('error',function(err){console.log(err);});

Это тоже не сработало.

Добавление try/catch вокруг сомнительного fs.statSynch() было лучшим решением для меня:

var stats;
try{
    stats = fs.statSync(path);
}catch(err){console.log(err);}

Затем это привело к исправлению кода (создание чистого пути var из папки и файла).

Ответ 6

Я нашел PM2 как лучшее решение для обработки серверов node, одиночных и нескольких экземпляров

Ответ 7

Одним из способов сделать это будет откручивание дочернего процесса и общение с родительским процессом через событие "сообщение".

В дочернем процессе, где возникает ошибка, поймите это с помощью 'uncaughtException', чтобы избежать сбоя приложения. Учтите, что Исключения, выброшенные из обработчика событий не будут обнаружены. Как только ошибка будет обнаружена безопасно, отправьте сообщение, например: {закончить: ложь}.

Родительский процесс будет прослушивать событие сообщения и снова отправить сообщение дочернему процессу для повторного запуска функции.

Ребенок:

// In child.js
// function causing an exception
  const errorComputation = function() {

        for (let i = 0; i < 50; i ++) {
            console.log('i is.......', i);
            if (i === 25) {
                throw new Error('i = 25');
            }
        }
        process.send({finish: true});
}

// Instead the process will exit with a non-zero exit code and the stack trace will be printed. This is to avoid infinite recursion.
process.on('uncaughtException', err => {
   console.log('uncaught exception..',err.message);
   process.send({finish: false});
});

// listen to the parent process and run the errorComputation again
process.on('message', () => {
    console.log('starting process ...');
    errorComputation();
})

Родительский процесс:

// In parent.js
    const { fork } = require('child_process');

    const compute = fork('child.js');

    // listen onto the child process
    compute.on('message', (data) => {
        if (!data.finish) {
            compute.send('start');
        } else {
            console.log('Child process finish successfully!')
        }
    });

    // send initial message to start the child process. 
    compute.send('start');