Właśnie zacząłem wypróbowywać node.js kilka dni temu. Uświadomiłem sobie, że Węzeł jest przerywany, ilekroć mam nieobsługiwany wyjątek w moim programie. Różni się to od zwykłego kontenera serwera, na który byłem narażony, gdzie umiera tylko wątek roboczy, gdy wystąpią nieobsługiwane wyjątki, a kontener nadal będzie mógł otrzymać żądanie. Rodzi to kilka pytań:
- Czy
process.on('uncaughtException')
to jedyny skuteczny sposób, aby się przed tym uchronić? - Będzie
process.on('uncaughtException')
złapać nieobsługiwany wyjątek podczas wykonywania procesów asynchronicznych, jak również? - Czy istnieje już moduł (np. Wysyłanie wiadomości e-mail lub zapis do pliku), który można wykorzystać w przypadku nieprzechwyconych wyjątków?
Byłbym wdzięczny za każdy wskaźnik / artykuł, który pokazywałby mi wspólne najlepsze praktyki postępowania z nieprzechwyconymi wyjątkami w node.js
try .. catch
i sprawdzisz, czy dzieje się tak również w przypadku wszystkich biblioteksetTimeout
lubsetInterval
czy coś w tym rodzaju zakopane gdzieś głęboko, które nie mogą być objęte kodem.Odpowiedzi:
Aktualizacja: Joyent ma teraz swój własny przewodnik . Poniższe informacje są bardziej podsumowaniem:
Błędy bezpiecznego „rzucania”
Idealnie chcielibyśmy uniknąć nieprzechwyconych błędów tak bardzo, jak to możliwe, zamiast dosłownie zgłaszać błąd, zamiast tego możemy bezpiecznie „wyrzucić” błąd, korzystając z jednej z następujących metod, w zależności od naszej architektury kodu:
W przypadku kodu synchronicznego, jeśli wystąpi błąd, zwróć błąd:
W przypadku kodu opartego na wywołaniu zwrotnym (tj. Asynchronicznym) pierwszym argumentem wywołania zwrotnego jest to
err
, że jeśli wystąpi błąd,err
to błąd, jeśli błąd się nie pojawi, toerr
jestnull
. Wszelkie inne argumenty następują poerr
argumencie:Dla pełnego wrażeń kodu, gdzie błąd może zdarzyć się wszędzie, zamiast rzucać się błąd, ogień
error
zdarzenie zamiast :Bezpiecznie „łapanie” błędów
Czasami jednak może nadal istnieć kod, który generuje błąd, który może prowadzić do nieprzechwyconego wyjątku i potencjalnej awarii naszej aplikacji, jeśli nie złapiemy go bezpiecznie. W zależności od naszej architektury kodu możemy użyć jednej z następujących metod, aby ją złapać:
Kiedy wiemy, gdzie występuje błąd, możemy owinąć tę sekcję w domenie node.js
Jeśli wiemy, gdzie występuje błąd, to kod synchroniczny i z jakiegokolwiek powodu nie można używać domen (być może starej wersji węzła), możemy użyć instrukcji try catch:
Uważaj jednak, aby nie używać
try...catch
w kodzie asynchronicznym, ponieważ nie zostanie wyłapany błąd asynchroniczny:Jeśli chcesz pracować
try..catch
w połączeniu z kodem asynchronicznym, podczas uruchamiania Węzła 7.4 lub nowszego możesz używaćasync/await
natywnie do pisania funkcji asynchronicznych.Kolejną rzeczą, na którą należy uważać,
try...catch
jest ryzyko zawinięcia wywołania zwrotnego zakończenia wtry
instrukcji:Gotcha jest bardzo łatwa, ponieważ kod staje się bardziej złożony. W związku z tym najlepiej jest albo użyć domen, albo zwrócić błędy, aby uniknąć (1) nieprzechwyconych wyjątków w kodzie asynchronicznym (2) próbowania przechwytywania wykonania, którego nie chcesz. W językach, które pozwalają na właściwe wątkowanie zamiast asynchronicznego stylu JavaScript w maszynie zdarzeń, nie stanowi to większego problemu.
Wreszcie w przypadku, gdy nieprzechwycony błąd wystąpi w miejscu, które nie zostało opakowane w domenie lub w instrukcji try catch, możemy sprawić, że nasza aplikacja nie ulegnie awarii przy użyciu
uncaughtException
nasłuchiwania (jednak może to spowodować, że aplikacja będzie w nieznanym stanie ):źródło
try catch
? Chciałbym poprzeć to dowodami. Naprawiono także przykład synchronizacji.Poniżej znajduje się podsumowanie i wybór z wielu różnych źródeł na ten temat, w tym przykład kodu i cytaty z wybranych postów na blogu. Pełna lista najlepszych praktyk znajduje się tutaj
Najlepsze praktyki obsługi błędów Node.JS
Liczba 1: Użyj obietnic do obsługi błędów asynchronicznych
TL; DR: Obsługa błędów asynchronicznych w stylu wywołania zwrotnego jest prawdopodobnie najszybszą drogą do piekła (inaczej piramidy zagłady). Najlepszym prezentem, jaki możesz dać kodowi, jest skorzystanie z renomowanej biblioteki obietnic, która zapewnia bardzo zwartą i znaną składnię kodu, taką jak try-catch
W przeciwnym razie: styl wywołania zwrotnego Node.JS, funkcja (err, odpowiedź), jest obiecującym sposobem na niemożliwy do utrzymania kod ze względu na połączenie obsługi błędów z przypadkowym kodem, nadmiernym zagnieżdżaniem i niewygodnymi wzorcami kodowania
Przykład kodu - dobrze
przykład kodu anty wzorzec - obsługa błędów w stylu wywołania zwrotnego
Cytat na blogu: „Mamy problem z obietnicami” (z pouchdb na blogu, w rankingu 11 słów kluczowych „Obietnice węzłowe”)
Number2: Używaj tylko wbudowanego obiektu Error
TL; DR: Kod, który generuje błędy jako ciąg znaków lub niestandardowy typ, jest dość powszechny - komplikuje to logikę obsługi błędów i interoperacyjność między modułami. Niezależnie od tego, czy odrzucisz obietnicę, wyrzucisz wyjątek, czy wyemitujesz błąd - użycie wbudowanego obiektu Error Node.JS zwiększa jednolitość i zapobiega utracie informacji o błędzie
W przeciwnym razie: podczas wykonywania jakiegoś modułu brak pewności, jaki rodzaj błędów w zamian wraca - znacznie utrudnia rozumowanie nadchodzącego wyjątku i obsługiwanie go. Warto nawet użyć niestandardowych typów do opisania błędów, które mogą prowadzić do utraty krytycznych informacji o błędach, takich jak ślad stosu!
Przykład kodu - robienie tego dobrze
przykładowy kod anty wzorca
Cytat na blogu: „Ciąg nie jest błędem” (z blogu, który zajął 6 miejsce w przypadku słów kluczowych „Obiekt błędu Node.JS”)
Liczba 3: Rozróżnij błędy operacyjne i programistyczne
TL; DR: Błędy operacyjne (np. API otrzymało niepoprawne dane wejściowe) odnoszą się do znanych przypadków, w których wpływ błędu jest w pełni zrozumiały i można go starannie rozpatrzyć. Z drugiej strony błąd programisty (np. Próba odczytania niezdefiniowanej zmiennej) odnosi się do nieznanych błędów kodu, które zmuszają do płynnego restartu aplikacji
W przeciwnym razie: zawsze możesz ponownie uruchomić aplikację, gdy pojawi się błąd, ale po co zawieść ~ 5000 użytkowników online z powodu drobnego i przewidywanego błędu (błędu operacyjnego)? wręcz przeciwnie, nie jest też idealne - utrzymanie aplikacji w stanie, gdy wystąpi nieznany problem (błąd programisty), może spowodować nieprzewidziane zachowanie. Rozróżnienie tych dwóch pozwala działać taktownie i stosować zrównoważone podejście oparte na danym kontekście
Przykład kodu - robienie tego dobrze
przykład kodu - oznaczenie błędu jako działającego (zaufanego)
Cytat na blogu : „W przeciwnym razie ryzykujesz stanem” (z bloga do debugowania, w rankingu 3 dla słów kluczowych „Nachwytany wyjątek Node.JS”)
Number4: Obsługuj błędy centralnie, ale nie w oprogramowaniu pośrednim
TL; DR: Logika obsługi błędów, taka jak poczta do administratora i rejestrowanie, powinna być zamknięta w dedykowanym i scentralizowanym obiekcie, do którego wywołują wszystkie punkty końcowe (np. Express middleware, zadania cron, testowanie jednostek), gdy wystąpi błąd.
W przeciwnym razie: Brak obsługi błędów w jednym miejscu doprowadzi do duplikacji kodu i prawdopodobnie do błędów, które są obsługiwane nieprawidłowo
Przykład kodu - typowy przepływ błędów
Cytat na blogu: „Czasami niższe poziomy nie mogą zrobić nic użytecznego oprócz propagowania błędu do osoby dzwoniącej” (z bloga Joyent, w rankingu 1 dla słów kluczowych „Obsługa błędów Node.JS”)
Number5: Błędy interfejsu API dokumentu za pomocą Swagger
TL; DR: Poinformuj dzwoniących API, które błędy mogą w zamian wrócić, aby mogli sobie z nimi dobrze poradzić bez awarii. Zwykle odbywa się to za pomocą ram dokumentacji API REST, takich jak Swagger
W przeciwnym razie: klient API może zdecydować o awarii i ponownym uruchomieniu tylko dlatego, że otrzymał błąd, którego nie mógł zrozumieć. Uwaga: osobą wywołującą interfejs API może być Ty (bardzo typowe w środowisku mikrousług)
Cytat na blogu: „Musisz powiedzieć swoim rozmówcom, jakie błędy mogą się zdarzyć” (z bloga Joyent, w rankingu 1 dla słów kluczowych „Logowanie do Node.JS”)
Numer 6: Zamknij proces z wdziękiem, gdy do miasta przybywa nieznajomy
TL; DR: Gdy wystąpi nieznany błąd (błąd programisty, patrz najlepsza praktyka nr 3) - nie ma pewności co do kondycji aplikacji. Powszechna praktyka sugeruje ostrożne ponowne uruchomienie procesu przy użyciu narzędzia „restartowania”, takiego jak Forever i PM2
W przeciwnym razie: gdy zostanie wykryty nieznany wyjątek, jakiś obiekt może być w złym stanie (np. Emiter zdarzeń, który jest używany globalnie i nie uruchamia już zdarzeń z powodu wewnętrznej awarii), a wszystkie przyszłe żądania mogą zawieść lub zachowywać się szaleńczo
Przykład kodu - decydowanie o awarii
Cytat z bloga: „Istnieją trzy szkoły myślenia na temat obsługi błędów” (z bloga jsrecipes)
Number7: Użyj dojrzałego programu rejestrującego, aby zwiększyć widoczność błędów
TL; DR: Zestaw dojrzałych narzędzi do rejestrowania, takich jak Winston, Bunyan lub Log4J, przyspieszy wykrywanie błędów i zrozumienie. Więc zapomnij o console.log.
W przeciwnym razie: przeglądanie przez plik console.logs lub ręcznie przez niechlujny plik tekstowy bez korzystania z narzędzi zapytań lub porządnej przeglądarki dziennika może być zajęty w pracy do późna
Przykład kodu - rejestrator Winston w akcji
Cytat na blogu: „Pozwala zidentyfikować kilka wymagań (dla loggera):” (z blogu strongblog)
Number8: Odkryj błędy i przestoje przy użyciu produktów APM
TL; DR: Produkty do monitorowania i wydajności (inaczej APM) proaktywnie oceniają twoją bazę kodu lub API, aby mogły automatycznie magicznie wyróżniać błędy, awarie i spowalniające brakujące części
W przeciwnym razie: możesz poświęcić wiele wysiłku na pomiar wydajności interfejsu API i przestojów, prawdopodobnie nigdy nie będziesz wiedział, jakie są twoje najwolniejsze części kodu w rzeczywistym świecie i jak wpływają one na UX
Cytat z bloga: „Segmenty produktów APM” (z bloga Yoni Goldberg)
Powyżej jest skróconą wersją - zobacz tutaj więcej najlepszych praktyk i przykładów
źródło
Możesz wyłapać niewyłapane wyjątki, ale ma on ograniczone zastosowanie. Zobacz http://debuggable.com/posts/node-js-dealing-with-uncaught-exceptions:4c933d54-1428-443c-928d-4e1ecbdd56cb
monit
,forever
Lubupstart
mogą być wykorzystane do ponownego uruchomienia procesu węzła kiedy się zawiesi. Pełne wdzięku zamknięcie jest najlepszym, na co możesz liczyć (np. Zapisz wszystkie dane w pamięci w nieprzechwyconym module obsługi wyjątków).źródło
Error
Zwrócenie an powoduje, że zwracana wartość jest polimorficzna, co niepotrzebnie zamazuje semantykę funkcji. Ponadto, nurkowanie przez 0 jest już obsługiwane w JavaScript dającInfinity
,-Infinity
lubNaN
, tam gdzie wartościtypeof === 'number'
. Można je sprawdzić za pomocą!isFinite(value)
. Ogólnie polecam nigdy nie zwracać błędu z funkcji. Lepsze pod względem czytelności kodu i konserwacji, aby rzucić lub zwrócić specjalną niepolimorficzną wartość w / spójną semantykę.Domeny nodejs to najnowocześniejszy sposób obsługi błędów w nodejs. Domeny mogą rejestrować zarówno błędy / inne zdarzenia, jak i obiekty tradycyjnie wyrzucane. Domeny zapewniają również funkcjonalność do obsługi wywołań zwrotnych z błędem przekazanym jako pierwszy argument za pomocą metody przechwytywania.
Podobnie jak w przypadku normalnej obsługi błędów typu try / catch, najlepiej jest wyrzucać błędy, gdy wystąpią, i blokować obszary, w których chcesz odizolować błędy od wpływu na resztę kodu. Sposobem na „zablokowanie” tych obszarów jest wywołanie domain.run z funkcją jako blok izolowanego kodu.
W kodzie synchronicznym wystarczy powyższe - gdy wystąpi błąd, albo przepuszczasz go, albo łapiesz i obsługujesz, przywracając dane, które chcesz przywrócić.
Gdy błąd występuje w asynchronicznym wywołaniu zwrotnym, musisz być w stanie w pełni obsłużyć wycofywanie danych (stan współużytkowany, dane zewnętrzne, takie jak bazy danych itp.). LUB musisz ustawić coś, co wskaże, że wystąpił wyjątek - gdziekolwiek zależy ci na tej fladze, musisz poczekać na zakończenie wywołania zwrotnego.
Część powyższego kodu jest brzydka, ale możesz stworzyć dla siebie wzory, aby uczynić go ładniejszym, np .:
AKTUALIZACJA (2013-09):
Powyżej używam przyszłości, która implikuje semantykę włókien , która pozwala czekać na futures w linii. To pozwala na użycie tradycyjnych bloków try-catch do wszystkiego - co uważam za najlepszy sposób. Jednak nie zawsze możesz to zrobić (np. W przeglądarce) ...
Istnieją również kontrakty terminowe, które nie wymagają semantyki włókien (które następnie działają z normalnym JavaScript do przeglądania). Można je nazwać futures, obietnicami lub odroczeniami (odtąd będę odnosił się do futures). Zwykłe biblioteki skryptów JavaScript zezwalają na propagowanie błędów między futures. Tylko niektóre z tych bibliotek pozwalają na prawidłową obsługę każdej rzuconej przyszłości, więc uważaj.
Przykład:
To naśladuje normalny try-catch, mimo że elementy są asynchroniczne. Wypisałby:
Zauważ, że nie wypisuje „3”, ponieważ został zgłoszony wyjątek, który przerywa ten przepływ.
Spójrz na obietnice bluebird:
Zauważ, że nie znalazłem wielu innych bibliotek oprócz tych, które poprawnie obsługują zgłoszone wyjątki. Na przykład jQuery jest odroczony - nie - moduł obsługi „fail” nigdy nie zgłasza wyjątku dla procedury obsługi „wtedy”, która moim zdaniem stanowi przełom.
źródło
Pisałem o tym niedawno na http://snmaynard.com/2012/12/21/node-error-handling/ . Nową funkcją węzła w wersji 0.8 są domeny i pozwalają łączyć wszystkie formy obsługi błędów w jedną łatwiejszą formę zarządzania. Możesz przeczytać o nich w moim poście.
Możesz także użyć czegoś takiego jak Bugsnag, aby śledzić swoje nieprzechwycone wyjątki i otrzymywać powiadomienia przez e-mail, czat lub utworzyć bilet dla nieprzechwyconego wyjątku (jestem współzałożycielem Bugsnag).
źródło
Chciałbym tylko dodać, że biblioteka Step.js pomaga radzić sobie z wyjątkami, zawsze przekazując ją do funkcji następnego kroku. Dlatego możesz mieć jako ostatni krok funkcję sprawdzającą pod kątem błędów w dowolnym z poprzednich kroków. Takie podejście może znacznie uprościć obsługę błędów.
Poniżej cytat ze strony github:
Co więcej, możesz użyć Step do kontrolowania wykonywania skryptów, aby sekcja czyszczenia była ostatnim krokiem. Na przykład, jeśli chcesz napisać skrypt kompilacji w węźle i zgłosić, ile czasu zajęło mu napisanie, możesz to zrobić w ostatnim kroku (zamiast próbować wykopać ostatnie wywołanie zwrotne).
źródło
Jednym z przypadków, w których użycie try-catch może być odpowiednie, jest użycie pętli forEach. Jest synchroniczny, ale jednocześnie nie można po prostu użyć instrukcji return w wewnętrznym zakresie. Zamiast tego można zastosować metodę try-catch, aby zwrócić obiekt Error w odpowiednim zakresie. Rozważać:
Jest to kombinacja podejść opisanych powyżej przez @balupton.
źródło
Po przeczytaniu tego postu jakiś czas temu zastanawiałem się, czy bezpiecznie jest używać domen do obsługi wyjątków na poziomie interfejsu API / funkcji. Chciałem ich użyć do uproszczenia kodu obsługi wyjątków w każdej funkcji asynchronicznej, którą napisałem. Obawiałem się, że użycie nowej domeny dla każdej funkcji spowoduje znaczne obciążenie. Moja praca domowa wydaje się wskazywać, że narzut jest minimalny i że wydajność jest w rzeczywistości lepsza w domenach niż w niektórych sytuacjach przy próbie złapania.
http://www.lighthouselogic.com/#/using-a-new-domain-for-each-async-function-in-node/
źródło
Błędy łapania zostały tutaj bardzo dobrze omówione, ale warto pamiętać, aby gdzieś je wylogować, aby można je było wyświetlić i naprawić.
Bunyan jest popularnym środowiskiem rejestrowania dla NodeJS - obsługuje pisanie w wielu różnych miejscach wyjściowych, co czyni go użytecznym do lokalnego debugowania, o ile unika się pliku console.log. W module obsługi błędów swojej domeny możesz wyrzucić błąd do pliku dziennika.
Może to zająć dużo czasu, jeśli masz wiele błędów i / lub serwerów do sprawdzenia, więc warto przyjrzeć się narzędziu, takim jak Raygun (zastrzeżenie, pracuję w Raygun), aby zgrupować błędy razem - lub użyć ich obu razem. Jeśli zdecydowałeś się użyć Raygun jako narzędzia, konfiguracja jest również dość łatwa
Twoja aplikacja powinna zostać zawieszona przy użyciu narzędzia takiego jak PM2 lub na zawsze, powinna się zawiesić, wylogować, co się stało, i uruchomić ponownie bez większych problemów.
źródło
Jeśli chcesz korzystać z usług w Ubuntu (Upstart): Węzeł jako usługa w Ubuntu 11.04 z upstart, monit i forever.js
źródło