Nice programing

오류시 node.js가 종료되지 않도록 설정

nicepro 2021. 1. 6. 20:47
반응형

오류시 node.js가 종료되지 않도록 설정


Socket.IO를 사용하여 websocket 지향 node.js 서버에서 작업하고 있습니다. 특정 브라우저가 서버에 대한 올바른 연결 절차를 따르지 않고 코드가 정상적으로 처리하도록 작성되지 않은 버그를 발견했습니다. 간단히 말해 설정되지 않은 개체에 대한 메서드를 호출하여 오류로 인해 서버.

내 관심사는 특히 버그가 아니라 이러한 오류가 발생하면 전체 서버가 다운된다는 사실입니다. 노드의 전역 수준에서 수행 할 수있는 작업이 있습니까? 오류가 발생하면 메시지를 기록하고 이벤트를 종료하지만 서버 프로세스는 계속 실행됩니다.

한 명의 영리한 사용자가 포함 된 대규모 코드베이스에서 발견되지 않은 오류를 악용하기 때문에 다른 사용자의 연결이 중단되는 것을 원하지 않습니다.


uncaughtException프로세스 객체 이벤트에 리스너를 연결할 수 있습니다 .

실제 Node.js API 참조 에서 가져온 코드 ( "프로세스"아래의 두 번째 항목) :

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 노드 / 문제


uncaughtException을 사용하는 것은 매우 나쁜 생각입니다.

가장 좋은 대안은 Node.js 0.8에서 도메인을 사용하는 것입니다. Node.js의 이전 버전을 사용하는 경우 영원히 사용 하여 프로세스를 다시 시작하거나 노드 클러스터사용 하여 여러 작업자 프로세스를 생성하고 uncaughtException 이벤트시 작업자를 다시 시작하십시오.

출처 : http://nodejs.org/api/process.html#process_event_uncaughtexception

경고 : 'uncaughtException'을 올바르게 사용

'uncaughtException'은 최후의 수단으로 만 사용하기위한 예외 처리를위한 조잡한 메커니즘입니다. 이벤트는 On Error Resume Next와 동일하게 사용되어서는 안됩니다. 처리되지 않은 예외는 본질적으로 응용 프로그램이 정의되지 않은 상태에 있음을 의미합니다. 예외에서 적절하게 복구하지 않고 애플리케이션 코드를 재개하려고하면 예상치 못한 추가 문제가 발생할 수 있습니다.

이벤트 핸들러 내에서 발생한 예외는 포착되지 않습니다. 대신 프로세스가 0이 아닌 종료 코드로 종료되고 스택 추적이 인쇄됩니다. 이것은 무한 재귀를 피하기위한 것입니다.

포착되지 않은 예외가 발생한 후 정상적으로 재개를 시도하는 것은 컴퓨터를 업그레이드 할 때 전원 코드를 뽑는 것과 비슷할 수 있습니다. 10 번 중 9 번은 아무 일도 일어나지 않지만 10 번 째에는 시스템이 손상됩니다.

'uncaughtException'의 올바른 사용은 프로세스를 종료하기 전에 할당 된 리소스 (예 : 파일 설명자, 핸들 등)의 동기식 정리를 수행하는 것입니다. 'uncaughtException'이후 정상 작동을 재개하는 것은 안전하지 않습니다.

uncaughtException의 발생 여부에 관계없이보다 안정적인 방식으로 충돌 된 응용 프로그램을 다시 시작하려면 별도의 프로세스에서 외부 모니터를 사용하여 응용 프로그램 오류를 감지하고 필요에 따라 복구하거나 다시 시작해야합니다.


나는 이것에 대해 많은 연구를했고 ( 여기 , 여기 , 여기 , 여기 참조 ) 귀하의 질문에 대한 대답은 Node가 당신이 발생할 수있는 모든 오류 시나리오를 잡을 하나의 오류 처리기를 작성하는 것을 허용하지 않는다는 것입니다. 체계.

express 와 같은 일부 프레임 워크 를 사용하면 특정 유형의 오류를 포착 할 수 있지만 (비동기 메서드가 오류 개체를 반환 할 때) 전역 오류 처리기로 포착 할 수없는 다른 조건이 있습니다. 이것은 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은 콜백 메서드에 대한 첫 번째 인수로 오류를 반환합니다. 이를 확인하고 발생했을 때 next (err)를 수행하면 기본 익스프레스 오류 처리기가 사용자가 수행하는 모든 작업을 인수하고 수행합니다 (예 : 사용자에게 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");
        }
    });
});

이 경우 코드에서 null 개체에 대한 메서드를 호출하는 경우 버그가 있습니다. 여기서 예외가 발생하고 전역 오류 처리기에 의해 포착되지 않으며 노드 앱이 종료됩니다. 현재 해당 서비스에서 요청을 실행중인 모든 클라이언트는 이유에 대한 설명없이 갑자기 연결이 끊어집니다. 비정상.

현재 Node에는이 경우를 처리하는 전역 오류 처리기 기능이 없습니다. try/catchasyn 콜백이 실행될 때까지 해당 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 년이 넘었고 미완성이며 이제 더 나은 방법이있을 수 있습니다. 모르겠습니다!)

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

비슷한 문제가있었습니다. Ivo의 대답은 좋습니다. 그러나 루프에서 오류를 포착하고 계속하려면 어떻게해야합니까?

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의 숨겨진 파일에 대해 이유를 모르겠습니다). process.on (...) 트릭으로 오류를 포착 할 수 있지만 루프가 중지됩니다.

I tried adding a handler directly:

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

This did not work either.

Adding a try/catch around the questionable fs.statSynch() was the best solution for me:

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

This then led to the code fix (making a clean path var from folder and file).


I found PM2 as the best solution for handling node servers, single and multiple instances


One way of doing this would be spinning the child process and communicate with the parent process via 'message' event.

In the child process where the error occurs, catch that with 'uncaughtException' to avoid crashing the application. Mind that Exceptions thrown from within the event handler will not be caught. Once the error is caught safely, send a message like: {finish: false}.

Parent Process would listen to the message event and send the message again to the child process to re-run the function.

Child Process:

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

Parent Process:

// 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'); 

ReferenceURL : https://stackoverflow.com/questions/4213351/make-node-js-not-exit-on-error

반응형