node.js - नोड.जेएस बेस्ट प्रैक्टिस अपवाद हैंडलिंग




exception-handling serverside-javascript (7)

मैंने कुछ दिनों पहले node.js को आजमाने की कोशिश की। मुझे एहसास हुआ है कि जब भी मेरे प्रोग्राम में एक अनचाहे अपवाद होता है तो नोड समाप्त हो जाता है। यह सामान्य सर्वर कंटेनर से अलग है जिसे मैं खुलासा कर चुका हूं जहां केवल कार्यकर्ता थ्रेड मर जाता है जब अनचाहे अपवाद होते हैं और कंटेनर अभी भी अनुरोध प्राप्त कर पाएगा। इससे कुछ सवाल उठते हैं:

  • process.on('uncaughtException') इसके खिलाफ सुरक्षा करने का एकमात्र प्रभावी तरीका है?
  • process.on('uncaughtException') होगी process.on('uncaughtException') के निष्पादन के दौरान भी अनचाहे अपवाद को पकड़ता है?
  • क्या कोई मॉड्यूल है जो पहले से ही बनाया गया है (जैसे ईमेल भेजना या फ़ाइल में लिखना) कि मैं बेजोड़ अपवादों के मामले में लाभ उठा सकता हूं?

मैं किसी भी पॉइंटर / आलेख की सराहना करता हूं जो मुझे node.js में बेजोड़ अपवादों को संभालने के लिए सामान्य सर्वोत्तम प्रथाओं को दिखाएगा


अपडेट करें: जॉयेंट के पास अब इस उत्तर में उल्लिखित अपनी मार्गदर्शिका है । निम्नलिखित जानकारी सारांश का अधिक है:

सुरक्षित रूप से "फेंकने" त्रुटियां

आदर्श रूप में हम जितना संभव हो सके असुरक्षित त्रुटियों से बचना चाहते हैं, जैसे कि सचमुच त्रुटि को फेंकने के बजाय, हम अपने कोड आर्किटेक्चर के आधार पर निम्न विधियों में से किसी एक का उपयोग करके त्रुटि को "फेंक" सुरक्षित रूप से सुरक्षित कर सकते हैं:

  • सिंक्रोनस कोड के लिए, यदि कोई त्रुटि होती है, तो त्रुटि लौटाएं:

    // Define divider as a syncrhonous function
    var divideSync = function(x,y) {
        // if error condition?
        if ( y === 0 ) {
            // "throw" the error safely by returning it
            return new Error("Can't divide by zero")
        }
        else {
            // no error occured, continue on
            return x/y
        }
    }
    
    // Divide 4/2
    var result = divideSync(4,2)
    // did an error occur?
    if ( result instanceof Error ) {
        // handle the error safely
        console.log('4/2=err', result)
    }
    else {
        // no error occured, continue on
        console.log('4/2='+result)
    }
    
    // Divide 4/0
    result = divideSync(4,0)
    // did an error occur?
    if ( result instanceof Error ) {
        // handle the error safely
        console.log('4/0=err', result)
    }
    else {
        // no error occured, continue on
        console.log('4/0='+result)
    }
    
  • कॉलबैक-आधारित (यानी एसिंक्रोनस) कोड के लिए, कॉलबैक का पहला तर्क err , अगर कोई त्रुटि त्रुटि होती है तो त्रुटि होती है, अगर कोई त्रुटि नहीं होती है तो त्रुटि null । कोई अन्य तर्क err तर्क का पालन करता है:

    var divide = function(x,y,next) {
        // if error condition?
        if ( y === 0 ) {
            // "throw" the error safely by calling the completion callback
            // with the first argument being the error
            next(new Error("Can't divide by zero"))
        }
        else {
            // no error occured, continue on
            next(null, x/y)
        }
    }
    
    divide(4,2,function(err,result){
        // did an error occur?
        if ( err ) {
            // handle the error safely
            console.log('4/2=err', err)
        }
        else {
            // no error occured, continue on
            console.log('4/2='+result)
        }
    })
    
    divide(4,0,function(err,result){
        // did an error occur?
        if ( err ) {
            // handle the error safely
            console.log('4/0=err', err)
        }
        else {
            // no error occured, continue on
            console.log('4/0='+result)
        }
    })
    
  • eventful कोड के लिए, जहां त्रुटि कहीं भी हो सकती है, त्रुटि को फेंकने के बजाय, error घटना को इसके बजाय आग लगाना:

    // Definite our Divider Event Emitter
    var events = require('events')
    var Divider = function(){
        events.EventEmitter.call(this)
    }
    require('util').inherits(Divider, events.EventEmitter)
    
    // Add the divide function
    Divider.prototype.divide = function(x,y){
        // if error condition?
        if ( y === 0 ) {
            // "throw" the error safely by emitting it
            var err = new Error("Can't divide by zero")
            this.emit('error', err)
        }
        else {
            // no error occured, continue on
            this.emit('divided', x, y, x/y)
        }
    
        // Chain
        return this;
    }
    
    // Create our divider and listen for errors
    var divider = new Divider()
    divider.on('error', function(err){
        // handle the error safely
        console.log(err)
    })
    divider.on('divided', function(x,y,result){
        console.log(x+'/'+y+'='+result)
    })
    
    // Divide
    divider.divide(4,2).divide(4,0)
    

सुरक्षित रूप से "पकड़ने" त्रुटियां

कभी-कभी, फिर भी ऐसा कोड हो सकता है जो किसी त्रुटि को कहीं फेंक देता है जो एक अपरिचित अपवाद और हमारे आवेदन की संभावित दुर्घटना का कारण बन सकता है अगर हम इसे सुरक्षित रूप से नहीं पकड़ते हैं। हमारे कोड आर्किटेक्चर के आधार पर हम इसे पकड़ने के लिए निम्न विधियों में से एक का उपयोग कर सकते हैं:

  • जब हम जानते हैं कि त्रुटि कहां हो रही है, तो हम उस अनुभाग को node.js डोमेन में लपेट सकते हैं

    var d = require('domain').create()
    d.on('error', function(err){
        // handle the error safely
        console.log(err)
    })
    
    // catch the uncaught errors in this asynchronous or synchronous code block
    d.run(function(){
        // the asynchronous or synchronous code that we want to catch thrown errors on
        var err = new Error('example')
        throw err
    })
    
  • अगर हम जानते हैं कि त्रुटि कहां हो रही है तो सिंक्रोनस कोड है, और जो भी कारण डोमेन (शायद नोड का पुराना संस्करण) का उपयोग नहीं कर सकता है, हम कोशिश पकड़ने का कथन का उपयोग कर सकते हैं:

    // catch the uncaught errors in this synchronous code block
    // try catch statements only work on synchronous code
    try {
        // the synchronous code that we want to catch thrown errors on
        var err = new Error('example')
        throw err
    } catch (err) {
        // handle the error safely
        console.log(err)
    }
    

    हालांकि, सावधान रहें कि try...catchtry...catch एसिंक्रोनस कोड में पकड़ें, क्योंकि एक असीमित रूप से फेंक दिया गया त्रुटि पकड़ा नहीं जाएगा:

    try {
        setTimeout(function(){
            var err = new Error('example')
            throw err
        }, 1000)
    }
    catch (err) {
        // Example error won't be caught here... crashing our app
        // hence the need for domains
    }
    

    try...catch बारे में सावधान रहना एक और बात है try...catch का try जैसे कि कथन कथन के अंदर अपना पूरा करने का कॉलबैक लपेटने का जोखिम:

    var divide = function(x,y,next) {
        // if error condition?
        if ( y === 0 ) {
            // "throw" the error safely by calling the completion callback
            // with the first argument being the error
            next(new Error("Can't divide by zero"))
        }
        else {
            // no error occured, continue on
            next(null, x/y)
        }
    }
    
    var continueElsewhere = function(err, result){
            throw new Error('elsewhere has failed')
    }
    
    try {
            divide(4, 2, continueElsewhere)
            // ^ the execution of divide, and the execution of 
            //   continueElsewhere will be inside the try statement
    }
    catch (err) {
            console.log(err.stack)
            // ^ will output the "unexpected" result of: elsewhere has failed
    }
    

    यह गोटाचा करना बहुत आसान है क्योंकि आपका कोड अधिक जटिल हो जाता है। इस प्रकार, डोमेन का उपयोग करना या त्रुटियों को वापस करने के लिए सबसे अच्छा है (1) एसिंक्रोनस कोड (2) में अनचाहे अपवादों को पकड़ने के प्रयास को पकड़ने की कोशिश करें जिसे आप नहीं चाहते हैं। ऐसी भाषाओं में जो जावास्क्रिप्ट की एसिंक्रोनस इवेंट-मशीन शैली की बजाय उचित थ्रेडिंग की अनुमति देते हैं, यह एक समस्या से कम है।

  • आखिरकार, ऐसे मामले में जहां किसी डोमेन में लपेटा नहीं गया था या किसी पकड़ने की कथन का प्रयास नहीं किया गया है, तो हम uncaughtException श्रोता का उपयोग करके हमारे एप्लिकेशन को क्रैश नहीं कर सकते हैं (हालांकि ऐसा करने से एप्लिकेशन को अज्ञात में रखा जा सकता है राज्य ):

    // catch the uncaught errors that weren't wrapped in a domain or try catch statement
    // do not use this in modules, but only in applications, as otherwise we could have multiple of these bound
    process.on('uncaughtException', function(err) {
        // handle the error safely
        console.log(err)
    })
    
    // the asynchronous or synchronous code that emits the otherwise uncaught error
    var err = new Error('example')
    throw err
    

आप बेजोड़ अपवादों को पकड़ सकते हैं, लेकिन यह सीमित उपयोग है। http://debuggable.com/posts/node-js-dealing-with-uncaught-exceptions:4c933d54-1428-443c-928d-4e1ecbdd56cb देखें

monit , forever या upstart को क्रैश होने पर नोड प्रक्रिया को पुनरारंभ करने के लिए उपयोग किया जा सकता है। एक सुंदर शटडाउन सबसे अच्छा है जिसके लिए आप उम्मीद कर सकते हैं (उदाहरण के लिए बिना अपवाद वाले हैंडलर में सभी इन-मेमोरी डेटा को सहेजें)।


एक उदाहरण जहां एक आचरण लूप का उपयोग करते समय एक प्रयास-पकड़ का उपयोग करना उपयुक्त हो सकता है। यह तुल्यकालिक है लेकिन साथ ही आप आंतरिक दायरे में वापसी विवरण का उपयोग नहीं कर सकते हैं। इसके बजाय एक प्रयास वस्तु को उचित दायरे में एक त्रुटि ऑब्जेक्ट वापस करने के लिए उपयोग किया जा सकता है। विचार करें:

function processArray() {
    try { 
       [1, 2, 3].forEach(function() { throw new Error('exception'); }); 
    } catch (e) { 
       return e; 
    }
}

यह ऊपर @balupton द्वारा वर्णित दृष्टिकोण का एक संयोजन है।


मैंने हाल ही में http://snmaynard.com/2012/12/21/node-error-handling/ पर इस बारे में लिखा था। संस्करण 0.8 में नोड की एक नई सुविधा डोमेन है और आपको त्रुटि प्रबंधन के सभी रूपों को एक आसान प्रबंधन फ़ॉर्म में संयोजित करने की अनुमति देती है। आप मेरे पद में उनके बारे में पढ़ सकते हैं।

आप अपने बेजोड़ अपवादों को ट्रैक करने के लिए बग्सगैग जैसे कुछ भी उपयोग कर सकते हैं और ईमेल, चैट रूम के माध्यम से अधिसूचित किए जा सकते हैं या बिना किसी अपवाद के लिए टिकट बनाया गया है (मैं बग्सनाग के सह-संस्थापक हूं)।


After reading this post some time ago I was wondering if it was safe to use domains for exception handling on an api / function level. I wanted to use them to simplify exception handling code in each async function I wrote. My concern was that using a new domain for each function would introduce significant overhead. My homework seems to indicate that there is minimal overhead and that performance is actually better with domains than with try catch in some situations.

http://www.lighthouselogic.com/#/using-a-new-domain-for-each-async-function-in-node/


Catching errors has been very well discussed here, but it's worth remembering to log the errors out somewhere so you can view them and fix stuff up.

​Bunyan is a popular logging framework for NodeJS - it supporst writing out to a bunch of different output places which makes it useful for local debugging, as long as you avoid console.log. ​ In your domain's error handler you could spit the error out to a log file.

var log = bunyan.createLogger({
  name: 'myapp',
  streams: [
    {
      level: 'error',
      path: '/var/tmp/myapp-error.log'  // log ERROR to this file
    }
  ]
});

This can get time consuming if you have lots of errors and/or servers to check, so it could be worth looking into a tool like Raygun (disclaimer, I work at Raygun) to group errors together - or use them both together. ​ If you decided to use Raygun as a tool, it's pretty easy to setup too

var raygunClient = new raygun.Client().init({ apiKey: 'your API key' });
raygunClient.send(theError);

पीएम 2 या हमेशा के लिए एक उपकरण का उपयोग करने के साथ पार किया, आपका ऐप क्रैश करने में सक्षम होना चाहिए, क्या हुआ और लॉग इन करें किसी भी बड़े मुद्दे के बिना रीबूट करें।






serverside-javascript