Node.js가 종료되기 직전에 정리 조치 수행


326

Node.js가 종료되기 직전에 어떤 이유로 든 Ctrl+ C, 예외 또는 다른 이유로 항상 무언가를하도록 지시하고 싶습니다 .

나는 이것을 시도했다 :

process.on('exit', function (){
    console.log('Goodbye!');
});

나는 그 과정을 시작하고 그것을 죽였고 아무 일도 일어나지 않았다. 나는 그것을 다시 시작하고 Ctrl+를 눌렀 C지만 여전히 아무 일도 일어나지 않았다 ...


답변:


511

최신 정보:

process.on('exit')다른 경우 ( SIGINT또는 처리되지 않은 예외) 에 대해 처리기 를 호출하여 호출 할 수 있습니다.process.exit()

process.stdin.resume();//so the program will not close instantly

function exitHandler(options, exitCode) {
    if (options.cleanup) console.log('clean');
    if (exitCode || exitCode === 0) console.log(exitCode);
    if (options.exit) process.exit();
}

//do something when app is closing
process.on('exit', exitHandler.bind(null,{cleanup:true}));

//catches ctrl+c event
process.on('SIGINT', exitHandler.bind(null, {exit:true}));

// catches "kill pid" (for example: nodemon restart)
process.on('SIGUSR1', exitHandler.bind(null, {exit:true}));
process.on('SIGUSR2', exitHandler.bind(null, {exit:true}));

//catches uncaught exceptions
process.on('uncaughtException', exitHandler.bind(null, {exit:true}));

4
같은 장소에서 Ctrl + C와 일반적인 종료를 모두 처리하는 방법이 있습니까? 아니면 두 개의 별도 처리기를 작성해야합니까? 처리되지 않은 예외와 같은 다른 유형의 종료는 어떻습니까?이 경우에는 특정 처리기가 있지만 동일한 처리기의 세 번째 사본으로 처리해야합니까?
Erel Segal-Halevi

1
@RobFox resume ()은 읽기 프로세스를 초기화합니다. Stdin은 기본적으로 일시 중지됩니다. 더 읽기 : github.com/joyent/node/blob/…
Emil Condrea

65
핸들러 에서 작업 을 must only수행 합니다.synchronousexit
Lewis

2
@KesemDavid beforeExit대신 이벤트 를 사용해야한다고 생각합니다 .
Lewis

22
이 솔루션에는 많은 문제가 있습니다. (1) 부모 프로세스에 신호를보고하지 않습니다. (2) 종료 코드를 상위 프로세스로 전달하지 않습니다. (3) Ctrl-C SIGINT를 무시하는 Emacs와 같은 어린이는 허용하지 않습니다. (4) 비동기 정리를 허용하지 않습니다. (5) stderr여러 정리 처리기 에서 단일 메시지를 조정하지 않습니다 . 필자는 아래의 cleanup.js 솔루션을 기반으로 github.com/jtlapp/node-cleanup 모듈을 작성 했지만 피드백을 기반으로 크게 수정했습니다. 도움이 되길 바랍니다.
Joe Lapp

180

아래 스크립트는 모든 종료 조건에 대해 단일 핸들러를 갖도록 허용합니다. 앱별 콜백 함수를 사용하여 사용자 지정 정리 코드를 수행합니다.

cleanup.js

// Object to capture process exits and call app specific cleanup function

function noOp() {};

exports.Cleanup = function Cleanup(callback) {

  // attach user callback to the process event emitter
  // if no callback, it will still exit gracefully on Ctrl-C
  callback = callback || noOp;
  process.on('cleanup',callback);

  // do app specific cleaning before exiting
  process.on('exit', function () {
    process.emit('cleanup');
  });

  // catch ctrl+c event and exit normally
  process.on('SIGINT', function () {
    console.log('Ctrl-C...');
    process.exit(2);
  });

  //catch uncaught exceptions, trace, then exit normally
  process.on('uncaughtException', function(e) {
    console.log('Uncaught Exception...');
    console.log(e.stack);
    process.exit(99);
  });
};

이 코드는 포착되지 않은 예외, Ctrl+ C및 일반 종료 이벤트를 인터셉트 합니다. 그런 다음 종료하기 전에 단일 선택적 사용자 정리 콜백 함수를 호출하여 단일 오브젝트로 모든 종료 조건을 처리합니다.

모듈은 단순히 다른 이벤트 이미 터를 정의하는 대신 프로세스 오브젝트를 확장합니다. 앱별 콜백이 없으면 정리는 기본적으로 op 기능이 없습니다. 이것은 Ctrl+로 종료 할 때 자식 프로세스가 실행 된 상태에서 사용하기에 충분했습니다 C.

SIGHUP과 같은 다른 종료 이벤트를 원하는대로 쉽게 추가 할 수 있습니다. 참고 : NodeJS 매뉴얼에 따라 SIGKILL은 리스너를 가질 수 없습니다. 아래 테스트 코드는 cleanup.js를 사용하는 다양한 방법을 보여줍니다.

// test cleanup.js on version 0.10.21

// loads module and registers app specific cleanup callback...
var cleanup = require('./cleanup').Cleanup(myCleanup);
//var cleanup = require('./cleanup').Cleanup(); // will call noOp

// defines app specific callback...
function myCleanup() {
  console.log('App specific cleanup code...');
};

// All of the following code is only needed for test demo

// Prevents the program from closing instantly
process.stdin.resume();

// Emits an uncaught exception when called because module does not exist
function error() {
  console.log('error');
  var x = require('');
};

// Try each of the following one at a time:

// Uncomment the next line to test exiting on an uncaught exception
//setTimeout(error,2000);

// Uncomment the next line to test exiting normally
//setTimeout(function(){process.exit(3)}, 2000);

// Type Ctrl-C to test forced exit 

@ Pier-LucGendreau이 특정 코드는 어디에 있습니까?
hownowbrowncow

11
나는이 코드가 필수 불가결하다는 것을 발견하고 수정하고 당신 과이 SO 답변을 인정하여 노드 패키지를 만들었습니다. @CanyonCasa, 괜찮습니다. 감사합니다! npmjs.com/package/node-cleanup
Joe Lapp

3
나는 청소를 좋아한다. 그러나 나는 process.exit (0); cons.org/cracauer/sigint.html 제 생각에는 커널이 파괴를 처리하도록해야합니다. SIGINT와 같은 방식으로 종료하지 않습니다. SIGINT가 2로 종료되지 않습니다. 오류 코드가 SIGINT에 잘못 적용되었습니다. 그들은 동일하지 않습니다. 실제로 Ctrl + C는 130으로 존재합니다. 2. 아닙니다. tldp.org/LDP/abs/html/exitcodes.html
Banjocat

5
@Banjocat의 링크 당 SIGINT 처리가 다른 프로세스와 잘 작동하도록 npmjs.com/package/node-cleanup 을 다시 작성 했습니다 . 또한 이제 호출하는 대신 상위 프로세스로 신호를 올바르게 릴레이합니다 process.exit(). 정리 핸들러는 이제 종료 코드 또는 신호의 기능으로 작동 할 수있는 유연성을 가지며 비동기 정리를 지원하거나 주기적 정리를 방지하기 위해 필요에 따라 정리 핸들러를 설치 제거 할 수 있습니다. 이제 위 코드와 거의 유사하지 않습니다.
Joe Lapp

3
나는 또한 (희망스럽게) 포괄적 인 테스트 스위트를 만들었다는 것을 잊었다.
Joe Lapp

29

이것은 처리 할 수있는 모든 종료 이벤트를 포착합니다. 지금까지는 매우 신뢰할 수 있고 깨끗해 보입니다.

[`exit`, `SIGINT`, `SIGUSR1`, `SIGUSR2`, `uncaughtException`, `SIGTERM`].forEach((eventType) => {
  process.on(eventType, cleanUpServer.bind(null, eventType));
})

대단해!
Andranik Hovesyan

대단한 일입니다. 지금 프로덕션 환경에서 사용하고 있습니다! 무리 감사!
랜디

20

"종료"는 노드가 이벤트 루프를 내부적으로 완료 할 때 트리거되는 이벤트이며, 외부에서 프로세스를 종료 할 때 트리거되지 않습니다.

당신이 찾고있는 것은 SIGINT에서 무언가를 실행하는 것입니다.

http://nodejs.org/api/process.html#process_signal_events 의 문서 는 예제를 제공합니다.

SIGINT 청취의 예 :

// Start reading from stdin so we don't exit.
process.stdin.resume();

process.on('SIGINT', function () {
  console.log('Got SIGINT.  Press Control-D to exit.');
});

참고 : 이것은 sigint를 방해하는 것으로 보이므로 코드를 완료하면 process.exit ()를 호출해야합니다.


1
같은 장소에서 Ctrl + C와 일반적인 종료를 모두 처리하는 방법이 있습니까? 아니면 두 개의 동일한 핸들러를 작성해야합니까?
Erel Segal-Halevi

참고로, kill 명령으로 노드를 종료 해야하는 경우 코드 kill -2가 전달됩니다 SIGINT. 우리는 txt 파일에 노드 로깅을 가지고 있으므로 Ctrl + C를 사용할 수 없으므로이 방법을 사용해야합니다.
Aust

9
function fnAsyncTest(callback) {
    require('fs').writeFile('async.txt', 'bye!', callback);
}

function fnSyncTest() {
    for (var i = 0; i < 10; i++) {}
}

function killProcess() {

    if (process.exitTimeoutId) {
        return;
    }

    process.exitTimeoutId = setTimeout(() => process.exit, 5000);
    console.log('process will exit in 5 seconds');

    fnAsyncTest(function() {
        console.log('async op. done', arguments);
    });

    if (!fnSyncTest()) {
        console.log('sync op. done');
    }
}

// https://nodejs.org/api/process.html#process_signal_events
process.on('SIGTERM', killProcess);
process.on('SIGINT', killProcess);

process.on('uncaughtException', function(e) {

    console.log('[uncaughtException] app will be terminated: ', e.stack);

    killProcess();
    /**
     * @https://nodejs.org/api/process.html#process_event_uncaughtexception
     *  
     * 'uncaughtException' should be used to perform synchronous cleanup before shutting down the process. 
     * It is not safe to resume normal operation after 'uncaughtException'. 
     * If you do use it, restart your application after every unhandled exception!
     * 
     * You have been warned.
     */
});

console.log('App is running...');
console.log('Try to press CTRL+C or SIGNAL the process with PID: ', process.pid);

process.stdin.resume();
// just for testing

4
이 답변에는 모든 영광이 필요하지만 설명이 없기 때문에 불행히도 투표권이 없습니다. 어떤 중요한 이 대답은에 대해, 사이먼이 말한다 , "리스너 함수는 동기 작업 만 수행해야합니다. Node.js를 프로세스는 여전히 이벤트 루프에서 대기 추가 작업을 포기하는 원인이되는 '출구'이벤트 리스너를 호출 한 후 즉시 종료됩니다. " ,이 대답은 그 한계를 극복!
xpt

7

https://github.com/jprichardson/node-deathdeath 패키지 를 언급하고 싶었 습니다.

예:

var ON_DEATH = require('death')({uncaughtException: true}); //this is intentionally ugly

ON_DEATH(function(signal, err) {
  //clean up code here
})

콜백 내에서 process.exit ()를 사용하여 프로그램을 명시 적으로 종료해야합니다. 이것은 나를 넘어 뜨렸다.
Nick Manning


0

창문을위한 멋진 해킹이 있습니다.

process.on('exit', async () => {
    require('fs').writeFileSync('./tmp.js', 'crash', 'utf-8')
});

0

다른 답변을 가지고 놀고 나면이 작업에 대한 나의 해결책이 있습니다. 이 방법을 구현하면 정리를 한 곳에서 중앙 집중화하여 정리를 두 번 처리 할 수 ​​없습니다.

  1. 다른 모든 종료 코드를 '종료'코드로 라우팅하고 싶습니다.
const others = [`SIGINT`, `SIGUSR1`, `SIGUSR2`, `uncaughtException`, `SIGTERM`]
others.forEach((eventType) => {
    process.on(eventType, exitRouter.bind(null, { exit: true }));
})
  1. exitRouter가하는 일은 process.exit ()를 호출하는 것입니다
function exitRouter(options, exitCode) {
   if (exitCode || exitCode === 0) console.log(`ExitCode ${exitCode}`);
   if (options.exit) process.exit();
}
  1. '종료'에서 새 기능으로 정리를 처리하십시오.
function exitHandler(exitCode) {
  console.log(`ExitCode ${exitCode}`);
  console.log('Exiting finally...')
}

process.on('exit', exitHandler)

데모 목적으로, 이것은 내 요지에 대한 링크 입니다. 파일에서 setTimeout을 추가하여 실행중인 프로세스를 가짜로 만듭니다.

실행 node node-exit-demo.js하고 아무 것도 수행하지 않으면 2 초 후에 로그가 표시됩니다.

The service is finish after a while.
ExitCode 0
Exiting finally...

서비스가 끝나기 전에로 종료 ctrl+C되면 다음과 같이 표시됩니다.

^CExitCode SIGINT
ExitCode 0
Exiting finally...

발생한 일은 Node 프로세스가 코드 SIGINT로 초기에 종료 된 다음 process.exit ()로 라우팅되고 마지막으로 종료 코드 0으로 종료됩니다.


-1

프로세스가 다른 노드 프로세스에 의해 생성 된 경우 :

var child = spawn('gulp', ['watch'], {
    stdio: 'inherit',
});

그리고 당신은 나중에 그것을 통해 그것을 죽이려고합니다 :

child.kill();

다음은 이벤트를 처리하는 방법입니다 [자식] :

process.on('SIGTERM', function() {
    console.log('Goodbye!');
});
당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.