2013-07-05 2 views
1

Я пытаюсь придумать общий (ухаживающий) способ обработки исключений в NodeJS, который не использует попытку catch из-за удара производительности. Я также хочу держаться подальше от библиотек, таких как streamline, которые пытаются сделать асинхронный код похожим на код синхронизации.Написание функций, связанных с доменом в NodeJS

Кажется, что Домены хорошо подходили к законопроекту, но я хотел пригласить комментарии/предложения по пути, который я предлагаю использовать. Существуют ли серьезные проблемы с этим подходом?

Я планирую сделать большинство моих функций асинхронными следовать схеме функции domainAware ниже:

function errorinAsync(options, callback){ 
    options = options || {}; 
    setTimeout(function(){ 
     return callback(new Error("This should be caught")); 
    },1000); 

} 
function domainAware(options, callback){ 
    if(domain.active){ 
     d = domain.active; 
    }else{ 
     d = domain.create(); 
     d.on('error', function(err){ 
      return callback(err); 
     }); 
    } 

    d.run(function(){ 
     //Some Synchronous code that might throw an exception; 
     var a = {b: 1, c: 2}; 
     var thing = JSON.stringify(a); 
     errorinAsync(null,d.intercept(function(err) { 
      return callback(null); 
     })); 
    }); 
} 

То, что я хочу сделать, это избежать бросать ошибку в функции асинхронной. Это в основном для ситуаций, когда у меня нет каких-либо особых исключений, которые я хочу обработать, но я хочу, чтобы исключения не терялись.

Я могу назвать это с контекстом домена:

var d = domain.create(); 
d.on('error', function(er) { 
    console.error('Caught error!', er); 
}); 
d.run(function() { 
    domainAware(null, d.intercept(function(err) { 
     console.log("all Done"); 
    })); 
}); 

Или без один:

domainAware(null, function(err){ 
    if(err){ 
     return console.log("Caught Error from Callback" + err); 
    } 
    console.log("all Done"); 
}); 

Это надуманный пример работает хорошо, но как насчет более сложных сценариев с большим количеством функций.

Обновление: # 1

Эквивалентная функция с помощью попытке поймать может быть:

function noHandling(callback){ 
    var a = {b: 1, c: 2}; 
    var thing = JSON.stringify(a); 
    errorinAsync(null,function(err) { 
     if(err) return callback(err); 
     return callback(null); 
    }); 
} 

function notDomainAware(options, callback){ 
    try{ 
     noHandling(callback); 
    }catch(err){ 
     callback(err); 
    } 
} 

Я буду делать некоторые тесты производительности на этих двух подходов, чтобы увидеть, если есть какая-то разница.

В отличие от производительности есть ли другая проблема с использованием подхода на основе домена? Пересмотренная версия функции уведомления домена может выглядеть так.

function domainAware(options, callback){ 
    var d = domain.active || domain.create().on('error', function(err){ return callback(err); }); 

    d.run(function(){ 
     //Some Synchronous code that might throw an exception; 
     var a = {b: 1, c: 2}; 
     var thing = JSON.stringify(a); 
     errorinAsync(null,d.intercept(function(err) { 
      return callback(null); 
     })); 
    }); 
} 

Мне нравится простота доменной версии, но более или менее эквивалентна? Конечно, вам нужно помнить, что использовать d.intercept или проверять ошибки любых обратных вызовов, но я могу справиться с этим.

+1

try catch только замедляет код вокруг него, поэтому попробуйте {doSomethingSlow()} НЕ замедлить вызванную функцию из-за обструкции оптимизации. – dandavis

+0

@ dandavis, я сделал обновление, чтобы включить эквивалентную версию try catch. Я проведу тестирование производительности, чтобы увидеть, есть ли какая-либо разница – Sudsy

ответ

2

UPDATE

Я сделал больше работы над этим и нашел отличный способ написания функций асинхронное с использованием доменов, которая устраняет большую часть обработки шаблонного исключения и работает лучше, чем пытаться обработки исключений поймать в некоторых случаи:

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

Это заменяет собой большую часть того, что написано в следующей почте. Фактически функция useExistingDomainifAvailable, которую я предложил ниже, имеет побочные эффекты, которые я не учитывал при написании этого оригинального ответа. Главное, что обработка ошибок всегда выполняется через короткое замыкание обратно в обработчик исключений домена, а не через цепочку обратного вызова.

UPDATE

Итак, я сделал некоторые тесты производительности и обнаружили, что версия домена на самом деле примерно такой же, как обертывание тела функции в попытке поймать:

Следующие две функции были используется во всех моих тестах:

function doSomethingAsync(options, callback){ 
    options = options || {}; 
    setTimeout(function(){ 
     return callback(null); 
    },1); 

} 

function callThroughDomain(fn, callback) { 
    var d = domain.create(); 
    d.on('error', function(er) { 
     console.error('Caught error!', er); 
    }); 
    d.run(function() { 
     fn(1000000, d.intercept(callback)); 
    }); 
} 

Я начал с управления:

function tryCatchCallback(j, callback) { 
     try{  
      var s = 0; 
      for (var i = 0; i < j; i++) s = i; 
      for (var i = 0; i < j; i++) s = i; 
      for (var i = 0; i < j; i++) s = i; 
      for (var i = 0; i < j; i++) s = i; 
      doSomethingAsync(null, function(err){ 
       //Just like domain.intercept, exceptions in the callback are handled 
       try{ 
        if(err) return callback(err); 
        callback(s); 
       }catch(ex){ 
        callback(ex); 
       } 

      }); 
     } 
     catch(ex) { 
      callback(ex); 
     } 
} 

Тест на котором был:

callThroughDomain(tryCatchCallback, function(){ 
    deferred.resolve(); 
}); 

Затем я попытался с помощью предварительно объявлено доменом:

function useExistingDomainifAvailable(j, callback) { 
    var d = domain.active || domain.create().on('error', function(err){ return callback(err); }); 

    d.run(function(){ 
     var s = 0; 
     for (var i = 0; i < j; i++) s = i; 
     for (var i = 0; i < j; i++) s = i; 
     for (var i = 0; i < j; i++) s = i; 
     for (var i = 0; i < j; i++) s = i; 
     doSomethingAsync(null, d.intercept(function(err){ 
      callback(s); 
     })); 
    }); 
} 

callThroughDomain(useExistingDomainifAvailable, function(){ 
    deferred.resolve(); 
}); 

Затем я попытался с кишками вызываемой функции с помощью внешнего try catch

function tryCatchOuter(j, callback) { 
    try{ 
     outer(1000000, callback); 
    }catch(e){ 
     console.log(e); 
    } 
} 

function outer(j, callback) { 
    var s = 0; 
    for (var i = 0; i < j; i++) s = i; 
    for (var i = 0; i < j; i++) s = i; 
    for (var i = 0; i < j; i++) s = i; 
    for (var i = 0; i < j; i++) s = i; 
    doSomethingAsync(null, function(err){ 
     //Again catching errors from callback 
     try{ 
      if(err) return callback(err); 
      callback(s); 
     }catch(ex){ 
      callback(ex) 
     } 

    }); 
} 

callThroughDomain(tryCatchOuter, function(){ 
    deferred.resolve(); 
}); 

Результаты моих тестов benchmark.js заключаются в следующем:

Контроль х 42.12 OPS/с ± 0,83% (38 проходит пробы)
useExistingDomainifAvailable х 41.98 OPS/с ± 6,67% (44 проходит пробы)
tryCatchOuter х 93.23 OPS/сек ± 2,07% (66 проходит пробы)
Быстрый тест tryCatchOuter

Показан значительный прирост производительности для сценария tryCatchOuter.

И для окончательного сравнения, пытающихся домены с внешним телом функции

function domainWithOuter(j, callback) { 
    var d = domain.active || domain.create().on('error', function(err){ return callback(err); }); 

    d.run(function(){ 
     outerNoHandler(j,callback); 
    }); 
} 

function outerNoHandler(j, callback) { 
    var s = 0; 
    for (var i = 0; i < j; i++) s = i; 
    for (var i = 0; i < j; i++) s = i; 
    for (var i = 0; i < j; i++) s = i; 
    for (var i = 0; i < j; i++) s = i; 
    doSomethingAsync(null, function(err){ 
      //Don't need try catch here 
      //Exceptions managed by domain 
      if(err) return callback(err); 
      callback(s); 
    }); 
} 

управления х 42.75 OPS/сек ± 1,06% (39 трасс выборочных)
useExistingDomainifAvailable х 42.86 OPS/сек ± 6,81% (38 проходит выборочные)
tryCatchOuter х 95.86 OPS/сек ± 2,35% (68 проходит пробы)
domainWithOuter х 94.65 OPS/сек ± 1,91% (67 проходит пробы)
быстрым является tryCatchOuter, domainWithOuter

Таким образом, по существу, использование домена такое же, как использование try catch с точки зрения производительности в этом случае с некоторыми различиями в синтаксисе.

Я думаю, потому что domain.run и doman.intercept используют try catch под обложками, их нужно использовать аналогичным образом с теми же оговорками о производительности.

Смежные вопросы