2015-04-23 12 views
6

Jestem stosunkowo nowym użytkownikiem do uruchamiania aplikacji produkcyjnych node.js, a ostatnio miałem problemy z przekroczeniem limitu czasu serwera.Problemy z przekroczeniem limitu czasu serwera Node.js (EC2 + Express + PM2)

Zasadniczo po pewnym czasie użytkowania aplikacja node.js przestaje odpowiadać na żądania. Nie widzę już nawet tras uruchamianych na mojej konsoli - to tak, jakby wszystko się zatrzymało, a połączenia HTTP od mojego klienta (iPhone'a z AFNetworkingiem) nie docierały już do serwera. Ale jeśli zrestartuję serwer aplikacji node.js, wszystko zacznie działać ponownie, dopóki rzeczy nie będą musiały się zatrzymać. Aplikacja nigdy się nie zawiesza, przestaje odpowiadać na żądania.

Nie otrzymuję żadnych błędów i upewniłem się, że obsługuję i rejestruję wszystkie błędy połączenia DB, więc nie jestem pewien, od czego zacząć. Pomyślałem, że może to mieć coś wspólnego z wyciekami pamięci, więc zainstalowałem węzeł-memwatch i skonfigurowałem detektor dla wycieków pamięci, ale to nie zostanie wywołane zanim mój serwer przestanie odpowiadać na żądania.

Każda wskazówka, co może się wydarzyć i jak rozwiązać ten problem?

Oto mój stos:

  • node.js na AWS EC2 Micro Instancji (za pomocą Express 4.0 + PM2)
  • Baza danych o objętości AWS RDS z systemem MySQL (przy użyciu węzła-mysql)
  • Sesje przechowywane w/Redis w tej samej instancji EC2, co aplikacja node.js
  • Klienci to iPhone'y uzyskujące dostęp do serwera za pośrednictwem AFNetworking

Po raz kolejny nie wystąpiły żadne błędy w żadnym z wyżej wymienionych modułów.

+0

Wygląda na to, że niektóre z twoich tras nie zwracają odpowiedzi i dlatego powieszają wszystko inne –

Odpowiedz

1

Przede wszystkim musisz trochę bardziej szczegółowo określać limity czasu.

  • timeout TCP: TCP dzieli wiadomości na pakiety, które są przesyłane jeden po drugim. Odbiornik musi potwierdzić, że odebrał pakiet. Jeśli odbiornik nie potwierdza otrzymania pakietu w określonym czasie, następuje retransmisja TCP, która ponownie wysyła ten sam pakiet. Jeśli zdarzy się to jeszcze kilka razy, nadawca zrezygnuje i zabije połączenie.

  • Limit czasu HTTP: Klient HTTP, taki jak przeglądarka, lub serwer działający jako klient (np. Wysyłający żądania do innych serwerów HTTP), może ustawić dowolny czas oczekiwania. Jeśli odpowiedź nie zostanie odebrana w tym okresie czasu, nastąpi jej rozłączenie i ustanowienie limitu czasu.

Teraz, istnieje wiele, wiele możliwych przyczyn tego stanu ... z bardziej trywialny mniej trywialny:

  • kalkulacja Niewłaściwy Content-Length: Jeśli wysyłasz wniosek z Content-Length: 20 nagłówek, co oznacza "Mam zamiar wysłać Ci 20 bajtów". Jeśli wyślesz 19, drugi koniec będzie czekać na pozostałe 1. Jeśli to zajmie zbyt długo ... timeout.

  • Za mało infrastruktury: Może powinieneś przydzielić więcej maszyn do swojej aplikacji. Jeśli wartość (total load/# of CPU cores) przekracza 1 lub zużycie pamięci jest wysokie, system może mieć zbyt dużą pojemność. Jednak czytaj dalej ...

  • Cichy wyjątek: Błąd został zgłoszony, ale nie był nigdzie rejestrowany. Żądanie nigdy nie zostało zakończone, co prowadzi do następnego elementu.

  • Wycieki zasobów: Każde żądanie należy załatwić do końca. Jeśli tego nie zrobisz, połączenie pozostanie otwarte. Ponadto, obiekt IncomingMesage (znany również jako: zwykle nazywany req w kodzie ekspresowym) pozostanie odwołany przez inne obiekty (np. Sam wyraz). Każdy z tych obiektów może korzystać z dużej ilości pamięci.

  • Węzeł pętli zdarzenia głodowego: Do tego dojdę na końcu.


przypadku nieszczelności pamięci objawy to: proces węzeł będzie za pomocą coraz większej ilości pamięci.

Na domiar złego, jeśli dostępna pamięć jest niska, a serwer jest źle skonfigurowany, aby użyć zamiany, Linux rozpocznie przenoszenie pamięci na dysk (zamiana), co jest bardzo intensywne we/wy i procesorze. Serwery nie powinny mieć włączonej zamiany.

cat /proc/sys/vm/swappiness 

spowoduje powrót do poziomu skonfigurowanej w systemie swobody (zmienia się od 0 do 100). Można go modyfikować w sposób trwały za pomocą /etc/sysctl.conf (wymaga ponownego uruchomienia) lub w sposób niestabilny, używając: sysctl vm.swappiness=10

Po stwierdzeniu przecieku pamięci należy pobrać zrzut pamięci i pobrać go do analizy. Sposób wykonania tej czynności można znaleźć w tej innej odpowiedzi Stackoverflow: Tools to analyze core dump from Node.js

W przypadku nieszczelności połączeń (wyciekło się połączenie, nie obsługując żądania do zakończenia), liczba połączeń z serwerem wzrastała. Możesz sprawdzić ustanowione połączenia, używając netstat -a -p tcp | grep ESTABLISHED | wc -l, aby zliczyć ustanowione połączenia.

Obecnie najgorszy problem to utrata pętli zdarzeń . Jeśli masz krótkotrwały węzeł kodu działa bardzo dobrze. Ale jeśli używasz procesora intensywnie pracującego i posiadasz funkcję, która zatrzymuje procesor przez zbyt długi czas ... jak 50 ms (50 ms, blokowanie, synchroniczny czas procesora, a nie asynchroniczny kod trwający 50 ms), operacje są obsługiwane przez pętlę zdarzeń, takie jak przetwarzanie żądań HTTP, zaczyna spadać z powrotem i w końcu wyczerpywać.

Sposobem na znalezienie wąskiego gardła procesora jest użycie profilera wydajności. nodegrind/qcachegrind to moje preferowane narzędzia do profilowania, ale inne preferują flamegrafy i inne. Jednak może być trudno uruchomić profiler w produkcji. Po prostu weź serwer programistyczny i zatrzaskuj go żądaniami. aka: test obciążenia. Jest na to wiele narzędzi.


Wreszcie inna droga do debugowania tego problemu jest:

env NODE_DEBUG=tls,net node <...arguments for your app>

węzeł posiada opcjonalne oświadczenia debugowania, które są włączone przez zmienną NODE_DEBUG środowiska. Ustawienie NODE_DEBUG na tls,net spowoduje, że węzeł będzie emitował informacje diagnostyczne dla modułów tls i net ... tak więc w zasadzie wszystko jest wysyłane lub odbierane. Jeśli jest czas oczekiwania, zobaczysz, skąd się bierze.

Źródło: Doświadczenie w utrzymaniu dużych wdrożeń usług węzłowych na lata.

Powiązane problemy