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 और एक ही स्थान पर सामान्य निकास दोनों को संभालने का एक तरीका है, या क्या मुझे दो अलग-अलग हैंडलर लिखने हैं? अन्य प्रकार के निकास के बारे में क्या, जैसे कि अखंड अपवाद - उस मामले के लिए एक विशिष्ट हैंडलर है, लेकिन क्या मुझे उसी हैंडलर की तीसरी प्रति के साथ संभालना चाहिए?
इरगल सहगल-हलेवी

1
@RobFox फिर से शुरू () पढ़ने की प्रक्रिया को आरंभ करता है। स्टड डिफ़ॉल्ट रूप से रोका गया है। आप इसे और अधिक पढ़ सकते हैं: github.com/joyent/node/blob/…
एमिल कंडरा

65
ध्यान दें कि आप हैंडलर में ऑपरेशन must onlyकरते हैंsynchronousexit
लुईस

2
@KesemDavid मुझे लगता है कि आपको beforeExitइसके बजाय घटना का उपयोग करना चाहिए ।
लुईस

22
इस समाधान में कई मुद्दे हैं। (1) यह पैतृक प्रक्रियाओं के संकेतों की सूचना नहीं देता है। (2) यह मूल प्रक्रिया के लिए निकास कोड को व्यक्त नहीं करता है। (३) यह ईमेक जैसे बच्चों के लिए अनुमति नहीं देता है जो Ctrl-C SIGINT को अनदेखा करते हैं। (4) यह अतुल्यकालिक सफाई की अनुमति नहीं देता है। (५) यह एक stderrसंदेश को कई सफाई संचालकों में समन्वयित नहीं करता है । मैंने एक मॉड्यूल लिखा है जो यह सब करता है, github.com/jtlapp/node-cleanup , मूल रूप से नीचे दिए गए क्लीनअप.जेएस समाधान पर आधारित है, लेकिन प्रतिक्रिया के आधार पर बहुत संशोधित किया गया है। मुझे उम्मीद है कि यह मददगार साबित होगा।
जो लैप

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और सामान्य निकास घटनाओं को स्वीकार करता है । यह तब बाहर निकलने से पहले एकल वैकल्पिक उपयोगकर्ता क्लीनअप कॉलबैक फ़ंक्शन को कॉल करता है, एक वस्तु के साथ सभी निकास स्थितियों को संभालता है।

मॉड्यूल बस एक और घटना उत्सर्जक को परिभाषित करने के बजाय प्रक्रिया ऑब्जेक्ट का विस्तार करता है। एक ऐप विशिष्ट कॉलबैक के बिना क्लीनअप डिफॉल्ट्स को नो ऑप फ़ंक्शन के लिए। यह मेरे उपयोग के लिए पर्याप्त था जहां Ctrl+ से बाहर निकलने पर बाल प्रक्रियाएं चल रही थीं C

आप आसानी से वांछित के रूप में अन्य बाहर की घटनाओं जैसे SITEUP जोड़ सकते हैं। नोट: प्रति NodeJS मैनुअल, SIGKILL में एक श्रोता नहीं हो सकता है। नीचे दिया गया परीक्षण कोड क्लीनअप.जेएस के उपयोग के विभिन्न तरीकों को प्रदर्शित करता है

// 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 

@ पियर-ल्यूकग्रेन्ड्रे यह विशिष्ट कोड कहां जाता है?
hownowbrowncow

11
मैंने इस कोड को अपरिहार्य पाया है और इसके लिए एक नोड पैकेज बनाया है, संशोधनों के साथ, आपको और एसओ उत्तर को जमा करता है। आशा है कि ठीक है, @CononCasa। धन्यवाद! npmjs.com/package/node-cleanup
जो लैप

3
मुझे सफाई पसंद है। लेकिन मैं इस प्रक्रिया को पसंद नहीं करता हूं। विदेशी (0); cons.org/cracauer/sigint.html मेरी भावना यह है कि आप कर्नेल को विनाश से निपटने दें। आप उसी तरह से बाहर नहीं निकल रहे हैं जैसे कि एक संकेत। SIGINT 2 से बाहर नहीं निकलता है। आप SIGINT को त्रुटि कोड के साथ गलत कर रहे हैं। वे समान नहीं हैं। वास्तव में Ctrl + C 130 के साथ मौजूद है। Not 2. tldp.org/LDP/abs/html/exitcodes.html
Banjocat

5
मैंने npmjs.com/package/node-cleanup को फिर से लिखा है ताकि @ Banjocat के लिंक के अनुसार SIGINT हैंडलिंग अन्य प्रक्रियाओं के साथ अच्छी तरह से काम करे। यह अब सही ढंग से कॉल करने के बजाय मूल प्रक्रिया के संकेतों पर निर्भर करता है process.exit()। क्लीनअप हैंडलर के पास अब एग्जिट कोड या सिग्नल के कार्य के रूप में व्यवहार करने की सुविधा है, और एसिंक्रोनस क्लीनअप का समर्थन करने या चक्रीय सफाई को रोकने के लिए आवश्यक के रूप में क्लीनअप हैंडलर की स्थापना रद्द की जा सकती है। यह अब उपरोक्त कोड से बहुत समानता रखता है।
जो लैप

3
उल्लेख करना भूल गया कि मैंने एक (उम्मीद) व्यापक परीक्षण सूट भी बनाया है।
जो लैप

29

यह हर एग्जिट इवेंट को पकड़ता है जो मुझे मिल सकता है। अब तक काफी विश्वसनीय और साफ लगता है।

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

यह कमाल का है!
एंड्रानिक होव्सियन

महान काम, मैं अभी उत्पादन में उपयोग कर रहा हूँ! बहुत बहुत धन्यवाद!
रैंडी

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

नोट: यह सिगंट को बाधित करने के लिए लगता है और आपको अपने कोड के साथ समाप्त होने पर process.exit () कॉल करना होगा।


1
क्या Ctrl + C और एक ही स्थान पर सामान्य निकास दोनों को संभालने का एक तरीका है? या मुझे दो समान हैंडलर लिखने हैं?
इरेल सेगल-हलेवी

बस एक नोट के रूप में, यदि आपको एक कमांड को मारना है तो नोड को समाप्त करना kill -2होगा SIGINT। हमें इसे इस तरह से करना होगा क्योंकि हमारे पास नोड लॉगिंग एक txt फ़ाइल में है इसलिए Ctrl + C संभव नहीं है।
ऑस्ट

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

बस deathयहां पैकेज का उल्लेख करना चाहता था : https://github.com/jprichardson/node-death

उदाहरण:

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

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

लगता है कि आपको कॉलबैक के भीतर प्रक्रिया के साथ प्रोग्राम से स्पष्ट रूप से बाहर निकलना होगा। इसने मुझे उलझा दिया।
निक मैनिंग


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. क्या बाहर निकलें रूटर कॉल कर रहा है 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)

डेमो उद्देश्य के लिए, यह मेरे जिस्ट का लिंक है। फ़ाइल में, मैं चल रही प्रक्रिया को नकली करने के लिए एक सेटटाइमआउट जोड़ता हूं।

यदि आप दौड़ते हैं 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...

क्या हुआ नोड कोड प्रक्रिया के साथ शुरू में निकली नोड प्रक्रिया है, फिर यह प्रोसेस.एक्सिट () और अंत में निकास कोड 0 के साथ बाहर निकलती है।


-1

उस मामले में जहां प्रक्रिया एक और नोड प्रक्रिया द्वारा पैदा की गई थी, जैसे:

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

और आप इसे बाद में मारने की कोशिश करते हैं:

child.kill();

इस तरह से आप इस घटना को संभाल सकते हैं [बच्चे पर]:

process.on('SIGTERM', function() {
    console.log('Goodbye!');
});
हमारी साइट का प्रयोग करके, आप स्वीकार करते हैं कि आपने हमारी Cookie Policy और निजता नीति को पढ़ और समझा लिया है।
Licensed under cc by-sa 3.0 with attribution required.