Jak zapobiec awariom node.js? try-catch nie działa


157

Z mojego doświadczenia wynika, że ​​serwer php wyrzuciłby wyjątek do dziennika lub na koniec serwera, ale node.js po prostu ulega awarii. Otaczanie mojego kodu za pomocą try-catch również nie działa, ponieważ wszystko jest wykonywane asynchronicznie. Chciałbym wiedzieć, co robią wszyscy inni na swoich serwerach produkcyjnych.

Odpowiedzi:


132

Inne odpowiedzi są naprawdę szalone, o czym można przeczytać w dokumentach Node pod adresem http://nodejs.org/docs/latest/api/process.html#process_event_uncaughtexception

Jeśli ktoś używa innych podanych odpowiedzi, przeczytaj Node Docs:

Zauważ, że uncaughtExceptionjest to bardzo prymitywny mechanizm obsługi wyjątków i może zostać usunięty w przyszłości

PM2

Przede wszystkim gorąco polecam zainstalowanie PM2na Node.js. PM2 świetnie radzi sobie z awariami i monitorowaniem aplikacji Node, a także z równoważeniem obciążenia. PM2 natychmiast uruchamia aplikację Node w przypadku awarii, zatrzymania z dowolnego powodu lub nawet po ponownym uruchomieniu serwera. Tak więc, jeśli pewnego dnia, nawet po zarządzaniu naszym kodem, aplikacja ulegnie awarii, PM2 może ją natychmiast ponownie uruchomić. Więcej informacji: Instalowanie i uruchamianie PM2

Wróćmy teraz do naszego rozwiązania, które zapobiega awariom samej aplikacji.

Więc po przejściu w końcu doszedłem do tego, co sugeruje sam dokument Node:

Nie używać uncaughtException, korzystanie domainsz clusterzamiast. Jeśli używasz uncaughtException, uruchom ponownie aplikację po każdym nieobsługiwanym wyjątku!

DOMAIN z klastrem

W rzeczywistości wysyłamy odpowiedź o błędzie na żądanie, które wywołało błąd, pozwalając innym zakończyć w ich normalnym czasie i przestać nasłuchiwać nowych żądań w tym procesie roboczym.

W ten sposób użycie domeny idzie ręka w rękę z modułem klastra, ponieważ proces główny może forować nowego pracownika, gdy pracownik napotka błąd. Zobacz poniższy kod, aby zrozumieć, o co mi chodzi

Używając Domaini elastycznie dzieląc nasz program na wiele procesów roboczych Cluster, możemy reagować lepiej i obsługiwać błędy z dużo większym bezpieczeństwem.

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

Chociaż Domainoczekuje na wycofanie i zostanie usunięty, gdy nowy zamiennik pojawi się zgodnie z dokumentacją Node

Ten moduł oczekuje na wycofanie. Po sfinalizowaniu zastępczego interfejsu API ten moduł zostanie całkowicie wycofany. Użytkownicy, którzy bezwzględnie muszą mieć funkcjonalność zapewnianą przez domeny, mogą na razie na niej polegać, ale powinni spodziewać się migracji do innego rozwiązania w przyszłości.

Jednak dopóki nowy zamiennik nie zostanie wprowadzony, Domain with Cluster jest jedynym dobrym rozwiązaniem, które sugeruje Node Documentation.

Aby dogłębnie zrozumieć Domaini Clusterprzeczytać

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

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

Dziękujemy @Stanley Luo za udostępnienie nam tego wspaniałego, dogłębnego wyjaśnienia na temat klastrów i domen

Klaster i domeny


9
Słowo ostrzeżenia, domena oczekuje na wycofanie: link . Sugerowaną metodą z dokumentacji Node jest użycie cluster: link .
Paul

4
restart your application after every unhandled exception!W przypadku, gdy 2000 użytkowników korzysta z serwera WWW węzła do przesyłania strumieniowego wideo, a 1 użytkownik dostał wyjątek, to ponowne uruchomienie nie przerwie pracy wszystkich innych użytkowników?
Vikas Bansal

2
@VikasBansal Tak, to z pewnością przerwie wszystkich użytkowników i dlatego używanie uncaughtExceptioni używanie Domainz nim jest złe Cluster, więc jeśli jeden użytkownik napotka wyjątek, tylko jego wątek zostanie usunięty z klastra i utworzy dla niego nowy. Nie musisz też ponownie uruchamiać serwera Node. Będąc po drugiej stronie, jeśli używasz uncaughtException, musisz ponownie uruchomić serwer za każdym razem, gdy którykolwiek z użytkowników napotka problem. Dlatego użyj domeny z klastrem.
Przewiewny

3
co powinniśmy zrobić, gdy domainzostanie całkowicie wycofany i usunięty?
Jas

3
Znalazłem ten samouczek dla tych, którzy nie rozumieją pojęcia clusteri workers: sitepoint.com/…
Stanley Luo

81

Umieściłem ten kod bezpośrednio pod moimi wymaganiami i deklaracjami globalnymi:

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

pracuje dla mnie. jedyne, co mi się w tym nie podoba, to to, że nie dostaję tylu informacji, ile bym, gdybym pozwolił, żeby coś się zawiesiło.


45
Jedna uwaga: ta metoda działa dobrze, ALE pamiętaj, że WSZYSTKIE odpowiedzi HTTP muszą zostać poprawnie zakończone. Oznacza to, że jeśli podczas obsługi żądania HTTP wystąpi nieprzechwycony wyjątek, nadal musisz wywołać metodę end () w obiekcie http.ServerResponse. Niezależnie od tego, jak to zrobisz, zależy od Ciebie. Jeśli tego nie zrobisz, żądanie zostanie zawieszone do czasu, gdy przeglądarka zrezygnuje. Jeśli masz wystarczająco dużo tych żądań, serwerowi może zabraknąć pamięci.
BMiner

3
@BMiner, czy możesz zapewnić lepszą implementację? Zauważyłem ten problem (zawieszenie żądania), więc to naprawdę nie jest lepsze niż po prostu ponowne uruchomienie serwera za pomocą foreverczy czegoś podobnego.
pixelfreak

6
Wymaga to dogłębnego wyjaśnienia. Wiem, że to jest do bani, ale za każdym razem, gdy wystąpi niezłapany wyjątek, serwer musi jak najszybciej zrestartować komputer. Tak naprawdę celem zdarzenia „uncaughtException” jest wykorzystanie go jako okazji do wysłania e-maila z ostrzeżeniem, a następnie użycia process.exit (1); aby zamknąć serwer. Możesz użyć wieczności lub czegoś takiego, aby ponownie uruchomić serwer. Wszelkie oczekujące żądania HTTP przekroczą limit czasu i zakończą się niepowodzeniem. Twoi użytkownicy będą na ciebie wściekli. Ale to najlepsze rozwiązanie. Dlaczego pytasz? Zamówienie stackoverflow.com/questions/8114977/ ...
BMiner

3
Aby uzyskać więcej informacji na temat nieprzechwyconego błędu, użyj: console.trace (err.stack);
Jesse Dunlap

2
OSTRZEŻENIE: Dokumentacja dla węzła mówi, że nie jest to niepewne, że nigdy nie powinieneś tego robić, ponieważ jest to szalenie niebezpieczne: nodejs.org/api/process.html#process_event_uncaughtexception
Jeremy Logan

28

Jak wspomniano tutaj , znajdziesz error.stackbardziej kompletny komunikat o błędzie, taki jak numer wiersza, który spowodował błąd:

process.on('uncaughtException', function (error) {
   console.log(error.stack);
});

12

Próbować supervisor

npm install supervisor
supervisor app.js

Lub możesz foreverzamiast tego zainstalować .

Wszystko to spowoduje odzyskanie serwera po awarii przez ponowne uruchomienie.

forever może być używany w kodzie, aby bezpiecznie odzyskać wszelkie awarie procesów.

Dokumentacja foreverzawiera solidne informacje o programowej obsłudze wyjścia / błędów.


9
Z pewnością nie może to być rozwiązanie… W czasie, gdy serwer nie działa, nie może odpowiadać na nowe przychodzące żądania. Wyjątek może zostać wyrzucony z kodu aplikacji - serwer musi odpowiedzieć błędem 500, a nie tylko zawiesić się i mieć nadzieję na ponowne uruchomienie.
Ant Kutschera

20
Więc jako haker, można się domyślić, że musi wysłać proste żądanie do serwera i pominąć parametr żądania - co prowadzi do undef w javascript, co powoduje awarię node.js. Za twoją sugestią mogę wielokrotnie zabijać całą twoją gromadę. Odpowiedzią jest sprawienie, aby aplikacja zawiodła z wdziękiem - tj. Obsłużyła nieprzechwycony wyjątek i nie uległa awarii. co by było, gdyby serwer obsługiwał wiele sesji VoIP? niedopuszczalne jest, aby się rozbił i spalił, a wszystkie istniejące sesje umarły wraz z nim. Twoi użytkownicy wkrótce odejdą.
Ant Kutschera

5
@AntKutschera dlatego wyjątki powinny być wyjątkowymi przypadkami. Wyjątki powinny uruchamiać się tylko w sytuacjach, w których nie można odzyskać i gdy proces musi się zawiesić. W takich wyjątkowych przypadkach należy skorzystać z innych środków . Ale rozumiem twój punkt widzenia. Tam, gdzie to możliwe, powinieneś z gracją zawieść. Są jednak przypadki, w których kontynuacja z uszkodzonym stanem spowoduje większe szkody.
Raynos

2
Tak, są tutaj różne szkoły myślenia. Sposób, w jaki się tego nauczyłem (Java zamiast Javascript), istnieją akceptowalne oczekiwania, których powinieneś się spodziewać, znane może jako wyjątki biznesowe, a także wyjątki lub błędy w czasie wykonywania, których nie powinieneś oczekiwać, że odzyskasz, na przykład brak pamięci. Jednym z problemów z niepowodzeniem wdzięcznym jest to, że pewna biblioteka, którą piszę, może zadeklarować, że zgłasza wyjątek w przypadku czegoś, co można odzyskać, na przykład gdy użytkownik może poprawić swoje dane wejściowe. w swojej aplikacji nie czytasz moich dokumentów i po prostu zawieszasz się, podczas gdy użytkownik mógł być w stanie odzyskać
Ant Kutschera

1
@AntKutschera Dlatego rejestrujemy wyjątki. Należy przeanalizować dzienniki produkcyjne pod kątem typowych wyjątków i dowiedzieć się, czy i jak można je odzyskać, zamiast pozwolić na awarię serwera. Użyłem tej metodologii w PHP, Ruby on Rails i Node. Niezależnie od tego, czy zakończysz proces, za każdym razem, gdy wyrzucisz błąd 500, wyrządzasz użytkownikom krzywdę. To nie jest praktyka specyficzna dla JavaScript ani węzła.
Eric Elliott

7

Użycie try-catch może rozwiązać niezłapane błędy, ale w niektórych złożonych sytuacjach nie będzie działać poprawnie, na przykład przechwytywanie funkcji asynchronicznej. Pamiętaj, że w Node każde wywołanie funkcji asynchronicznej może zawierać potencjalną operację awarii aplikacji.

Używanie uncaughtExceptionjest obejściem, ale jest uznawane za nieefektywne i prawdopodobnie zostanie usunięte w przyszłych wersjach Node, więc nie licz na to.

Idealnym rozwiązaniem jest użycie domeny: http://nodejs.org/api/domain.html

Aby upewnić się, że aplikacja działa i działa nawet w przypadku awarii serwera, wykonaj następujące czynności:

  1. użyj klastra węzłów do rozwidlenia wielu procesów na rdzeń. Więc jeśli jeden proces umarł, inny proces zostanie automatycznie uruchomiony. Sprawdź: http://nodejs.org/api/cluster.html

  2. użyj domeny do przechwycenia operacji asynchronicznej zamiast używania try-catch lub uncatcht. Nie mówię, że próba złapania lub nie złapany to zła myśl!

  3. używaj na zawsze / Supervisor do monitorowania swoich usług

  4. dodaj demona do uruchamiania aplikacji węzła: http://upstart.ubuntu.com

mam nadzieję że to pomoże!


4

Wypróbuj moduł węzła pm2, jest on spójny i ma świetną dokumentację. Menedżer procesów produkcyjnych dla aplikacji Node.js z wbudowanym systemem równoważenia obciążenia. proszę unikać wyjątku uncaughtException dla tego problemu. https://github.com/Unitech/pm2


`zrestartuj aplikację po każdym nieobsługiwanym wyjątku!` W przypadku, gdy 2000 użytkowników używa serwera WWW węzła do przesyłania strumieniowego wideo, a 1 użytkownik dostał wyjątek, to ponowne uruchomienie nie przerwie pracy wszystkich innych użytkowników?
Vikas Bansal

Byłem bardzo szczęśliwy, kiedy odkryłem PM2. świetne oprogramowanie
Mladen Janjetovic

0

UncaughtException to „bardzo prymitywny mechanizm” (tak prawdziwy), a domeny są teraz przestarzałe. Jednak nadal potrzebujemy mechanizmu do wychwytywania błędów wokół (logicznych) domen. Biblioteka:

https://github.com/vacuumlabs/yacol

może ci w tym pomóc. Przy odrobinie dodatkowego pisania możesz mieć ładną semantykę domeny w całym kodzie!


0

Działa świetnie na restify:

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();
});
Korzystając z naszej strony potwierdzasz, że przeczytałeś(-aś) i rozumiesz nasze zasady używania plików cookie i zasady ochrony prywatności.
Licensed under cc by-sa 3.0 with attribution required.