Як запобігти збоїв у node.js? пробний лов не працює


157

З мого досвіду, сервер php видав би виняток до журналу або до кінця сервера, але node.js просто виходить з ладу. Оточення мого коду спробним уловом не працює, оскільки все робиться асинхронно. Я хотів би знати, що роблять всі інші на своїх виробничих серверах.

Відповіді:


132

Інші відповіді справді божевільні, як ви можете прочитати у власних документах Node за адресою http://nodejs.org/docs/latest/api/process.html#process_event_uncaughtexception

Якщо хтось використовує інші заявлені відповіді, прочитайте Node Docs:

Зауважте, що uncaughtExceptionце дуже сильний механізм для обробки винятків, і він може бути усунений у майбутньому

PM2

Перш за все, я б настійно рекомендую встановити PM2для Node.js. PM2 дійсно чудово справляється з збоями та моніторингом додатків Node, а також з балансуванням навантаження. PM2 негайно запускає додаток Node кожного разу, коли він виходить з ладу, зупиняється з будь-якої причини або навіть при перезапуску сервера. Отже, якщо коли-небудь навіть після управління нашим кодом, програма вийде з ладу, PM2 може негайно її перезапустити. Для отримання додаткової інформації Встановлення та запуск PM2

Тепер повернемося до нашого рішення щодо запобігання збоїв самого додатка.

Отже, пройшовши все, я нарешті придумав те, що сам документ Node пропонує:

Чи не слід використовувати uncaughtException, використовувати domainsз clusterзамість цього. Якщо ви все-таки використовуєте uncaughtException, перезавантажте свою програму після кожного необробленого винятку!

ДОМАН з кластером

Що ми насправді робимо, - це надіслати відповідь про помилку на запит, який викликав помилку, при цьому дозволяючи іншим закінчитись у звичайний час, і перестати слухати нові запити у цього працівника.

Таким чином, використання домену йде рука об руку з модулем кластера, оскільки головний процес може виправити нового працівника, коли працівник зіткнеться з помилкою. Дивіться код нижче, щоб зрозуміти, що я маю на увазі

Використовуючи 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 API заміни цей модуль буде повністю застарілим. Користувачі, які абсолютно повинні мати функціонал, який надають домени, на даний момент можуть покластися на нього, але повинні очікувати, що в майбутньому доведеться перейти до іншого рішення.

Але поки нова заміна не буде введена, Домен із кластером - єдине хороше рішення, що пропонує Документація щодо вузлів.

Для поглибленого розуміння Domainта Clusterчитання

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

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

Дякуємо @Stanley Luo за те, що він поділився цим чудовим глибоким поясненням щодо кластеру та доменів

Кластер та домени


9
Слово застереження, Домен очікує припинення: посилання . Запропонований метод із документів Node полягає у використанні cluster: link .
Пол

4
restart your application after every unhandled exception!Якщо 2000 користувачів використовують веб-сервер вузла для трансляції відео, і 1 користувач отримав виняток, то перезапуск не перерве всіх інших користувачів?
Викас Бансал

2
@VikasBansal Так , що, безумовно , переривання всі користувачі , і тому це погано використовувати uncaughtExceptionі використовувати Domainз Clusterзамість так, якщо один користувач стикається виняток , щоб тільки його потік видаляється з кластера і створив новий для нього. І вам також не потрібно перезавантажувати сервер Node. Якщо ви використовуєте іншу сторону, uncaughtExceptionвам доведеться перезавантажувати сервер кожного разу, коли будь-який ваш користувач стикається з проблемою. Отже, використовуйте Домен із кластером.
Ейрі

3
що робити, коли domainповністю викрадено та видалено?
Жас

3
Знайдено цей підручник для тих, хто не розуміє поняття clusterта workers: sitepoint.com/…
Стенлі Лу

81

Я ставлю цей код прямо під мої заяви про вимоги та глобальні декларації:

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

працює для мене. Єдине, що мені не подобається в цьому, - це я не отримую стільки інформації, скільки б, якби я просто дозволив річ розбитися.


45
Слово застереження: цей метод працює чудово, Але пам'ятайте, що ВСІ відповіді HTTP потрібно закінчити належним чином. Це означає, що якщо під час обробки HTTP-запиту виникає вимкнено виняток, ви все одно повинні викликати end () на об’єкт http.ServerResponse. Однак ви реалізовуєте це, залежати від вас. Якщо цього не зробити, запит зависне, поки браузер не відмовиться. Якщо цих запитів у вас достатньо, у сервера може не вистачити пам'яті.
BMiner

3
@BMiner, ви могли б забезпечити кращу реалізацію? Я помітив цю проблему (запит висить), тому це насправді не краще, ніж просто перезапустити сервер foreverчи щось.
pixelfreak

6
Це вимагає глибокого пояснення. Я знаю, що це відстійно, але всякий раз, коли трапляється вимкнене виключення, ваш сервер потребує перезавантаження ASAP. Дійсно, мета події "uncaughtException" полягає в тому, щоб використовувати її як можливість надіслати повідомлення з попередженням, а потім використовувати process.exit (1); для відключення сервера. Ви можете використовувати назавжди або щось подібне для перезавантаження сервера. Будь-які очікувані запити HTTP закінчуються та закінчуються. Ваші користувачі будуть злі на вас. Але, це найкраще рішення. Чому ти питаєш? Оформити замовлення stackoverflow.com/questions/8114977 / ...
BMiner

3
Для отримання додаткової інформації про невідомі помилки використовуйте: console.trace (err.stack);
Джессі Данлап

2
ПОПЕРЕДЖЕННЯ: Документація для вузла говорить, що не сумнівно, що ви ніколи цього не робите, бо це божевільно небезпечно: nodejs.org/api/process.html#process_event_uncaughtexception
Джеремі Логан

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, а не просто виходити з ладу і сподіватися, що його перезапустять.
Мурашка Кучера

20
Отже, як хакер, можна зрозуміти, що їм потрібно надіслати простий запит на сервер і пропустити параметр запиту - це призводить до undef в javascript, що призводить до збоїв node.js. За вашою пропозицією я можу вбити весь ваш кластер кілька разів. Відповідь полягає в тому, щоб програма вийшла з ладу витончено - тобто обробляти невдале виняток і не виходити з ладу. що робити, якщо сервер обробляв багато сеансів VoIP? неприпустимо, щоб воно руйнувалося і спалювалося, і щоб усі ці існуючі сеанси померли з ним. ваші користувачі незабаром підуть.
Мураш Кучера

5
@AntKutschera, тому винятки мають бути винятковими випадками. Винятки повинні стріляти тільки в тих ситуаціях , коли ви не можете відновити і де процес повинен врізатися. Для вирішення цих виняткових випадків слід використовувати інші засоби . Але я бачу вашу думку. Ви повинні виграшно вийти з ладу, де це можливо. Однак є випадки, коли продовження корумпованого стану нанесе більше шкоди.
Райнос

2
Так, тут є різні школи думки. У тому, як я це навчився (Java, а не Javascript), є прийнятні очікування, яких слід очікувати, відомі, можливо, як винятки з бізнесу, і тоді є винятки або помилки під час виконання, коли не слід очікувати відновлення, як, наприклад, з пам'яті. Однією з проблем, що не виходять з ладу, є те, що деяка бібліотека, яку я пишу, може заявити, що вона є винятком у випадку чогось відновленого, скажімо, де користувач міг би виправити свої дані. у вашому додатку ви не читаєте мої документи та просто
виходите з ладу

1
@AntKutschera Ось чому ми реєструємо винятки. Ви повинні проаналізувати свої виробничі журнали на поширені винятки та з’ясувати, чи можна і як ви могли відновитись із них, замість того, щоб давати збій серверу. Я використовував цю методологію з PHP, Ruby on Rails та Node. Незалежно від того, виходите з процесу чи ні, кожен раз, коли ви видаєте помилку 500, ви робите своїм користувачам сумлінну послугу. Це не практика JavaScript або вузол.
Ерік Елліотт

7

Використання пробного лову може вирішити неприховані помилки, але в деяких складних ситуаціях це не виконає роботу, як-от ловіння функції асинхронізації. Пам'ятайте, що у "Вузлі" будь-які виклики функції асинхронізації можуть містити потенційну операцію збою програми.

Використання uncaughtExceptionє способом вирішення, але воно визнається неефективним і, ймовірно, буде видалено у майбутніх версіях Node, тому не розраховуйте на нього.

Ідеальним рішенням є використання домену: http://nodejs.org/api/domain.html

Щоб переконатися, що ваш додаток працює, навіть ваш сервер вийшов з ладу, виконайте наступні дії:

  1. використовувати кластер вузлів для розщеплення декількох процесів на ядро. Отже, якщо один процес загинув, інший процес буде автоматично завантажений. Перевірте: http://nodejs.org/api/cluster.html

  2. використовувати домен, щоб ловити операцію асинхронізації замість того, щоб пробувати ловити або виконувати без уваги. Я не кажу, що спроба лову або невдаху - це погана думка!

  3. використовувати навіки / супервізор для контролю ваших послуг

  4. додайте демон для запуску програми на вузлі: http://upstart.ubuntu.com

сподіваюся, що це допомагає!


4

Спробуйте модуль вузла pm2, він цілком відповідає і має чудову документацію. Менеджер виробничих процесів для додатків Node.js із вбудованим балансиром навантаження. будь ласка, уникайте невластивоговиконання цієї проблеми. https://github.com/Unitech/pm2


`перезавантажте програму після кожного необробленого винятку!` Якщо 2000 користувачів використовують веб-сервер вузла для трансляції відео, а 1 користувач отримав виняток, то перезапуск не перерве всіх інших користувачів?
Вікас Бансал

Я був такий щасливий, коли виявив PM2. чудова програма
Младен Яньєтович

0

UncaughtException - це "дуже сильний механізм" (так правда), і домени тепер застарілі. Однак нам все-таки потрібен якийсь механізм, щоб вловлювати помилки навколо (логічних) доменів. Бібліотека:

https://github.com/vacuumlabs/yacol

може допомогти вам це зробити. Маючи трохи зайвого написання, ви можете мати приємну семантику домену по всьому коду!


0

Чудово працює на рестифікувати:

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();
});
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.