node.js가 충돌하는 것을 어떻게 방지합니까? try-catch가 작동하지 않습니다


157

내 경험상 PHP 서버는 로그 또는 서버 쪽에서 예외를 throw하지만 node.js는 단순히 충돌합니다. 모든 코드가 비동기 적으로 수행되므로 try-catch로 코드를 둘러 쌀 수 없습니다. 프로덕션 서버에서 다른 사람들이 무엇을하는지 알고 싶습니다.

답변:


132

http://nodejs.org/docs/latest/api/process.html#process_event_uncaughtexception 에서 Node의 자체 문서를 읽을 수 있으므로 다른 답변은 정말 미쳤습니다.

누군가 다른 명시된 답변을 사용하는 경우 노드 문서를 읽으십시오.

참고 uncaughtException예외 처리를위한 매우 조질기구이며 향후 제거 될 수있다

PM2

우선, 나는 높게 설치하는 것이 좋습니다 PM2위해 Node.js. PM2는 충돌을 처리하고 노드 앱을 모니터링하고로드 밸런싱에 정말 좋습니다. PM2는 충돌이 발생하거나 어떤 이유로 든 서버가 다시 시작될 때마다 Node 앱을 즉시 시작합니다. 따라서 언젠가 코드를 관리 한 후에도 앱이 중단되면 PM2가 즉시 다시 시작할 수 있습니다. 자세한 정보는 PM2 설치 및 실행

이제 앱 자체의 충돌을 막기위한 솔루션으로 돌아 왔습니다.

그래서 나는 마침내 Node 문서 자체가 제안하는 것을 생각해 냈습니다.

사용하지 마십시오 uncaughtException, 사용 domains으로 cluster대신. 를 사용하는 경우 uncaughtException처리되지 않은 모든 예외 후에 응용 프로그램을 다시 시작하십시오!

DOMAIN클러스터

우리가 실제로하는 일은 오류를 발생시킨 요청에 오류 응답을 보내면서 다른 사람들이 정상적인 시간에 끝내도록하고 해당 작업자의 새 요청을 듣지 않는 것입니다.

이러한 방식으로, 작업자 프로세스에 오류가 발생하면 마스터 프로세스가 새 작업자를 분기 할 수 있으므로 도메인 사용은 클러스터 모듈과 함께 사용됩니다. 내가 의미하는 바를 이해하려면 아래 코드를 참조하십시오

을 사용하고을 사용하여 Domain프로그램을 여러 작업자 프로세스로 분리하는 복원력을 통해 Cluster보다 적절하게 대응하고 훨씬 더 안전하게 오류를 처리 할 수 ​​있습니다.

var cluster = require('cluster');
var PORT = +process.env.PORT || 1337;

if(cluster.isMaster) 
{
   cluster.fork();
   cluster.fork();

   cluster.on('disconnect', function(worker) 
   {
       console.error('disconnect!');
       cluster.fork();
   });
} 
else 
{
    var domain = require('domain');
    var server = require('http').createServer(function(req, res) 
    {
        var d = domain.create();
        d.on('error', function(er) 
        {
            //something unexpected occurred
            console.error('error', er.stack);
            try 
            {
               //make sure we close down within 30 seconds
               var killtimer = setTimeout(function() 
               {
                   process.exit(1);
               }, 30000);
               // But don't keep the process open just for that!
               killtimer.unref();
               //stop taking new requests.
               server.close();
               //Let the master know we're dead.  This will trigger a
               //'disconnect' in the cluster master, and then it will fork
               //a new worker.
               cluster.worker.disconnect();

               //send an error to the request that triggered the problem
               res.statusCode = 500;
               res.setHeader('content-type', 'text/plain');
               res.end('Oops, there was a problem!\n');
           } 
           catch (er2) 
           {
              //oh well, not much we can do at this point.
              console.error('Error sending 500!', er2.stack);
           }
       });
    //Because req and res were created before this domain existed,
    //we need to explicitly add them.
    d.add(req);
    d.add(res);
    //Now run the handler function in the domain.
    d.run(function() 
    {
        //You'd put your fancy application logic here.
        handleRequest(req, res);
    });
  });
  server.listen(PORT);
} 

비록 Domain중단 보류하고 새로운 교체로 노드의 문서에 명시되어 제공으로 제거됩니다

이 모듈은 더 이상 사용되지 않습니다. 교체 API가 완료되면이 모듈은 더 이상 사용되지 않습니다. 도메인이 제공하는 기능을 반드시 갖추어야하는 사용자는 당분간 도메인에 의존 할 수 있지만 향후 다른 솔루션으로 마이그레이션해야합니다.

그러나 새로운 대체품이 소개되지 않을 때까지 Domain with Cluster가 Node Documentation에서 제안하는 유일한 솔루션입니다.

깊이 이해 Domain하고 Cluster읽으려면

https://nodejs.org/api/domain.html#domain_domain (Stability: 0 - Deprecated)

https://nodejs.org/api/cluster.html

클러스터 및 도메인에 대한이 훌륭한 설명을 공유해 주신 @Stanley Luo에게 감사드립니다.

클러스터 및 도메인


9
경고 : 도메인이 지원 중단 보류 중입니다 ( link) . Node 문서에서 제안 된 방법은 cluster : link 를 사용하는 것 입니다.
Paul

4
restart your application after every unhandled exception!2000 명의 사용자가 비디오 스트리밍에 노드 웹 서버를 사용하고 있고 1 명의 사용자에게 예외가 발생한 경우 다시 시작해도 다른 모든 사용자가 중단되지 않습니까?
Vikas Bansal

2
@VikasBansal 네, 반드시 모든 사용자를 방해 할 것입니다. 그래서 한 명의 사용자가 예외에 직면하여 자신의 스레드 만 클러스터에서 제거되어 새로운 스레드를 생성하기 때문에 대신 사용 uncaughtException하고 사용 Domain하는 Cluster것이 좋지 않습니다 . 또한 노드 서버를 다시 시작할 필요가 없습니다. 다른 쪽에서는 사용하는 경우 사용자가 uncaughtException문제에 직면 할 때마다 서버를 다시 시작해야합니다. 따라서 도메인에 클러스터를 사용하십시오.
Airy

3
domain더 이상 사용되지 않고 제거 된 경우 어떻게해야 합니까?
Jas

3
의 개념을 이해하지 못하는 사람들을 위해이 자습서를 찾을 수 clusterworkers: sitepoint.com/...
스탠리 루오

81

이 코드를 require 문과 전역 선언 바로 아래에 넣습니다.

process.on('uncaughtException', function (err) {
  console.error(err);
  console.log("Node NOT Exiting...");
});

나를 위해 작동합니다. 내가 마음에 들지 않는 유일한 것은 내가 물건을 추락 시키면 내가 할만 큼 많은 정보를 얻지 못한다는 것입니다.


45
주의 사항 :이 방법은 훌륭하게 작동하지만 모든 HTTP 응답을 올바르게 종료해야한다는 것을 기억하십시오. 즉, HTTP 요청을 처리하는 동안 포착되지 않은 예외가 발생하면 여전히 http.ServerResponse 오브젝트에서 end ()를 호출해야합니다. 그러나 이것을 구현하는 것은 당신에게 달려 있습니다. 이렇게하지 않으면 브라우저가 종료 될 때까지 요청이 중단됩니다. 이러한 요청이 충분하면 서버 메모리가 부족할 수 있습니다.
BMiner

3
@BMiner, 더 나은 구현을 제공 할 수 있습니까? 이 문제 (요청 중단)를 발견 했으므로 실제로 서버를 다시 시작하는 것보다 좋은 것이 아닙니다 forever.
pixelfreak

6
이에 대한 자세한 설명이 필요합니다. 나는 이것이 짜증나다는 것을 알고 있지만 잡히지 않는 예외가 발생할 때마다 서버를 최대한 빨리 재부팅해야합니다. 실제로 'uncaughtException'이벤트의 목적은 이벤트를 경고 이메일을 발송 한 후 process.exit (1); 서버를 종료합니다. 서버를 다시 시작하기 위해 영원히 또는 이와 유사한 것을 사용할 수 있습니다. 보류중인 HTTP 요청이 시간 초과되고 실패합니다. 당신의 사용자는 당신에게 화를 낼 것입니다. 그러나 최상의 솔루션입니다. 왜 물어? 체크 아웃 stackoverflow.com/questions/8114977/…
BMiner

3
포착되지 않은 오류에서 자세한 정보를 얻으려면 다음을 사용하십시오. console.trace (err.stack);
Jesse Dunlap

2
경고 : 노드에 대한 문서는 확실하지 않은 용어로, 절대 위험한
Jeremy Logan

28

언급 한 바와 같이 여기에서 찾을 수 있습니다 error.stack같은 오류가 발생한 줄 번호로보다 완벽한 오류 메시지를 제공합니다 :

process.on('uncaughtException', function (error) {
   console.log(error.stack);
});

12

시험 supervisor

npm install supervisor
supervisor app.js

또는 forever대신 설치할 수 있습니다 .

이 작업은 서버를 다시 시작하여 충돌 할 때 서버를 복구하는 것입니다.

forever 코드 내에서 충돌이 발생한 모든 프로세스를 정상적으로 복구 할 수 있습니다.

forever워드 프로세서 프로그램 처리 종료 / 오류에 고체 정보가 있습니다.


9
확실히 이것은 해결책이 될 수 없습니다 ... 서버가 다운되는 시간에 새로운 수신 요청에 응답 할 수 없습니다. 응용 프로그램 코드에서 예외가 발생할 수 있습니다. 서버는 충돌뿐만 아니라 다시 시작되기를 바라는 것이 아니라 500 오류로 응답해야합니다.
앤트 Kutschera

20
따라서 해커로서 서버에 간단한 요청을 보내고 요청 매개 변수를 누락해야한다는 것을 알 수 있습니다. 이로 인해 javascript가 undef되어 node.js가 중단됩니다. 당신의 제안으로, 나는 전체 클러스터를 반복적으로 죽일 수 있습니다. 정답은 응용 프로그램을 정상적으로 실패하게하는 것입니다. 즉 잡히지 않은 예외를 처리하고 충돌하지 않습니다. 서버가 많은 VoIP 세션을 처리하는 경우 어떻게됩니까? 충돌하거나 타거나 기존의 모든 세션이 종료되는 것은 허용되지 않습니다. 당신의 사용자는 곧 떠날 것입니다.
Ant Kutschera

5
@AntKutschera는 예외가 예외적 인 경우가되는 이유입니다. 복구 할 수없고 프로세스 중단 되어야하는 상황에서만 예외가 발생합니다. 이러한 예외적 인 경우 를 처리하기 위해 다른 수단을 사용해야합니다 . 그러나 나는 당신의 요점을 봅니다. 가능하면 정상적으로 실패해야합니다. 그러나 손상된 상태를 계속하면 더 많은 피해를 입는 경우가 있습니다.
Raynos

2
예, 여기에는 다른 사고 학교가 있습니다. 내가 배운 방식 (자바 스크립트가 아닌 Java)에는 비즈니스 예외라고 할 수있는 기대할만한 기대치가 있으며 메모리 부족과 같이 복구하지 않아야하는 런타임 예외 또는 오류가 있습니다. 정상적으로 실패하지 않는 한 가지 문제는 내가 작성한 일부 라이브러리가 복구 가능한 무언가가있는 경우 예외를 throw한다고 선언 할 수 있다는 것입니다. 사용자가 입력을 수정할 수있는 곳. 귀하의 응용 프로그램에서, 당신은 내 문서를 읽지 못하고 사용자가 복구 할 수 있었던 곳에서 충돌합니다
Ant Kutschera

1
@AntKutschera 이것이 예외를 기록하는 이유입니다. 일반적인 예외에 대해 프로덕션 로그를 분석하고 서버가 충돌하지 않도록 복구 할 수 있는지 여부와 방법을 알아 내야합니다. PHP, Ruby on Rails 및 Node에서이 방법론을 사용했습니다. 프로세스를 종료하는지 여부에 관계없이 500 오류가 발생할 때마다 사용자에게 서비스 장애가 발생합니다. 이것은 JavaScript 또는 노드 별 연습이 아닙니다.
Eric Elliott

7

try-catch를 사용하면 잡히지 않는 오류를 해결할 수 있지만 일부 복잡한 상황에서는 비동기 함수를 잡는 등의 작업을 제대로 수행하지 못합니다. Node에서 모든 비동기 함수 호출에는 잠재적 인 앱 충돌 작업이 포함될 수 있습니다.

사용 uncaughtException은 해결 방법이지만 비효율적 인 것으로 인식되어 향후 버전의 Node에서 제거 될 가능성이 있으므로 사용하지 마십시오.

이상적인 해결책은 도메인을 사용하는 것입니다 : http://nodejs.org/api/domain.html

서버가 다운 된 경우에도 앱이 작동하고 실행되도록하려면 다음 단계를 사용하십시오.

  1. 노드 클러스터를 사용하여 코어 당 여러 프로세스를 분기하십시오. 따라서 한 프로세스가 종료되면 다른 프로세스가 자동 부팅됩니다. 체크 아웃 : http://nodejs.org/api/cluster.html

  2. try-catch 또는 uncaught를 사용하는 대신 domain을 사용하여 비동기 작업을 포착하십시오. 나는 try-catch 또는 uncaught가 나쁜 생각이라고 말하는 것이 아닙니다!

  3. 영원히 / 관리자를 사용하여 서비스 모니터링

  4. 데몬을 추가하여 노드 앱을 실행하십시오 : http://upstart.ubuntu.com

도움이 되었기를 바랍니다!


4

pm2 노드 모듈을 사용해보십시오. 일관성이 뛰어나고 훌륭한 문서가 있습니다. 로드 밸런서가 내장 된 Node.js 앱의 프로덕션 프로세스 관리자 이 문제에 대한 uncaughtException을 피하십시오. https://github.com/Unitech/pm2


`처리되지 않은 예외마다 응용 프로그램을 다시 시작하십시오!`2000 명의 사용자가 비디오 스트리밍에 노드 웹 서버를 사용하고 있고 1 명의 사용자가 예외를받은 경우 다시 시작해도 다른 모든 사용자가 중단되지 않습니까?
Vikas Bansal

PM2를 발견했을 때 정말 기뻤습니다. 소프트웨어의 큰 조각
믈라덴 Janjetovic

0

UncaughtException은 "매우 조잡한 메커니즘"(그렇기 때문에) 도메인은 더 이상 사용되지 않습니다. 그러나 (논리적) 도메인 주변에서 오류를 잡는 메커니즘이 여전히 필요합니다. 도서관:

https://github.com/vacuumlabs/yacol

당신이 이것을 도울 수 있습니다. 약간의 추가 작성으로 코드 주변에 멋진 도메인 의미를 가질 수 있습니다!


0

restify에서 잘 작동합니다.

server.on('uncaughtException', function (req, res, route, err) {
  log.info('******* Begin Error *******\n%s\n*******\n%s\n******* End Error *******', route, err.stack);
  if (!res.headersSent) {
    return res.send(500, {ok: false});
  }
  res.write('\n');
  res.end();
});
당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.