2013-04-27 15 views
7

Coś, o czym pomyślałem:Co stanie się, gdy masz nieskończoną pętlę w kodzie widoku Django?

Załóżmy, że piszę kod widoku dla mojej witryny Django, a ja popełniam błąd i utworzę nieskończoną pętlę.

Ilekroć ktoś spróbuje uzyskać dostęp do widoku, robotnik przypisany do żądania (czy to pracownik Geventa lub wątek Python) pozostanie w pętli przez czas nieokreślony.

Jeśli dobrze rozumiem, serwer wyśle ​​błąd czasowy do klienta po 30 sekundach. Ale co stanie się z pracownikiem Pythona? Czy będzie działać bezterminowo? To brzmi groźnie!

Wyobraź sobie, że mam serwer, na którym mam przydzielonych 10 pracowników. Pozwoliłem, aby to działało iw pewnym momencie klient próbuje uzyskać dostęp do widoku z nieskończoną pętlą. Pracownik zostanie przydzielony do niego i będzie skutecznie martwy do następnego uruchomienia serwera. Niebezpieczne jest to, że na początku tego nie zauważyłem, ponieważ strona będzie po prostu niepostrzeżenie wolniejsza, mając 9 pracowników zamiast 10. Ale może się to zdarzyć znowu i znowu przez długi czas, może miesięcy. Strona będzie stopniowo postępować wolniej, aż w końcu z jednym tylko robotnikiem będzie bardzo wolno.

Restart serwera rozwiązałby problem, ale nie chciałbym, aby funkcjonalność mojej witryny była zależna od restartu serwera.

Czy to prawdziwy problem? Czy istnieje sposób, aby tego uniknąć?

Aktualizacja: Chciałbym również bardzo doceniam sposób, aby zrobić StackTrace gwintu/pracownika, który utknął w nieskończonej pętli, więc mogę mieć, że wysłana do mnie, więc będę sobie sprawę z problemu . (Nie wiem, jak to zrobić, ponieważ nie podniesiono żadnego wyjątku).

Aktualizacja osobom mówiącym o skutkach "Unikaj pisania kodu, który ma nieskończone pętle": W przypadku, gdyby nie było oczywiste, że nie spędzam wolnego czasu celowo umieszczając nieskończone pętle w moim kodzie. Kiedy takie rzeczy się zdarzają, są błędami, a błędy można zminimalizować, ale nigdy nie można ich całkowicie uniknąć. Chcę wiedzieć, że nawet gdy popełnię błąd, będzie sieć bezpieczeństwa, która powiadomi mnie i pozwoli mi rozwiązać problem.

+2

ciekawe lektury: http: // stackoverflow. com/questions/8685695/how-do-i-run-long-term-infinite-python-processes –

+0

Zaktualizowałem moją odpowiedź, mam nadzieję, że teraz odpowiada na twoje pytanie :) –

Odpowiedz

4

To jest prawdziwy problem. W przypadku geventu, z powodu przełączania kontekstów, może nawet natychmiast zatrzymać twoją stronę internetową.

Wszystko zależy od środowiska. Na przykład, podczas uruchamiania django w produkcji przez uwsgi można ustawić harakiri - czyli czas w sekundach, po którym wątek obsługujący żądanie zostanie zabity, jeśli nie zakończył obsługi odpowiedzi. Zdecydowanie zaleca się ustawienie takiej wartości, aby poradzić sobie z błędnymi żądaniami lub złym kodem. Takie zdarzenie jest zgłaszane w rejestrze uwsgi. Sądzę, że inne rozwiązania do uruchamiania Django w produkcji mają podobne opcje.

W przeciwnym razie, z powodu architektury sieci, rozłączenie klienta nie zatrzyma nieskończonej pętli, a domyślnie nie będzie żadnej odpowiedzi - tylko nieskończone ładowanie. Różne opcje przekroczenia limitu czasu (z których jedna to harakiri) mogą powodować wyświetlanie limitu czasu połączenia - na przykład php ma (o ile pamiętam) domyślny limit czasu wynoszący 30 sekund i zwróci 504 limit czasu bramki. Limit czasu rozłączenia gniazd zależy od ustawień serwera http i nie zatrzyma wątku aplikacji, spowoduje jedynie zamknięcie gniazda klienta.

Jeśli nie używasz gevent (lub innych zielonych nici), nieskończona pętla będzie zajmować 100% dostępnej mocy procesora (ograniczona do jednego rdzenia), prawdopodobnie zjedząc coraz więcej pamięci, więc twoja witryna będzie działać całkiem spowolnienie i/lub limit czasu naprawdę szybko. Sam Django nie zna czasu żądania, więc - jak wspomniano wcześniej - stos środowiska produkcyjnego jest sposobem, aby temu zapobiec. W przypadku uwsgi, droga do celu jest http://uwsgi-docs.readthedocs.org/en/latest/Options.html#harakiri-verbose.

Harakiri ma śladu druku stosu zabitych przeróbki: (https://uwsgi-docs.readthedocs.org/en/latest/Tracebacker.html?highlight=harakiri) prosto do uwsgi zalogować, a ze względu na system alarmowy można otrzymywać powiadomienia za pośrednictwem poczty e-mail (http://uwsgi-docs.readthedocs.org/en/latest/AlarmSubsystem.html)

+0

Opcja Harakiri jest krokiem we właściwym kierunku, ponieważ zatrzymuje serwer, który utknął, ale nie pomaga znaleźć źródła problemu i go naprawić. To, czego chcę, to wysłanie do mnie e-maila dotyczącego obraźliwego pracownika, aby móc go sprawdzić i naprawić problem w kodzie. –

+0

Harakiri drukuje informacje o stosie i żądaniach, a system alarmowy nginx umożliwia powiadamianie pocztą e-mail. Zaktualizowana odpowiedź z linkami. –

+0

Przepraszam, miałem na myśli system alarmowy uwsgi oczywiście :) –

0

Tak, twoja analiza jest prawidłowa. Wątek/proces roboczy będzie kontynuowany. Co więcej, jeśli nie ma czekania/uśpienia w pętli, będzie on hog CPU. Inne wątki/proces dostaną bardzo mało procesora, co spowoduje, że cała witryna będzie wolna.

Ponadto, nie sądzę, serwer będzie wysyłać jawnie błąd limitu czasu do klienta. Jeśli ustawiony jest limit czasu TCP, połączenie TCP zostanie zamknięte.

Klient może również ustawić czas oczekiwania, aby uzyskać odpowiedź, która może się pojawić.

Unikanie takiego kodu jest najlepszym sposobem na uniknięcie takiego kodu. Możesz także mieć jakieś narzędzie do monitorowania na serwerze, aby wyszukać użycie procesora/pamięci i powiadomić o nieprawidłowej aktywności, abyś mógł podjąć działanie.

2

Właśnie przetestowałem to na serwerze programistycznym Django.

Wyniki:

  • nie daje czas, po upływie 30 sekund.(może to być spowodowane tym, że nie jest to serwer produkcyjny).
  • Pozostaje w załadowaniu do momentu zamknięcia strony.

Zgaduję, że jednym ze sposobów uniknięcia tego, bez po prostu uniknięcia takiego kodu, byłoby użycie wątków, aby kontrolować limity czasu i móc zatrzymać wątek.

Może coś takiego:

import threading 
from django.http import HttpResponse 

class MyThread(threading.Thread): 
    def __init__(self): 
     threading.Thread.__init__(self) 
    def run(self): 
     print "your possible infinite loop code here" 

def possible_loop_view(request): 
    thread = MyThread() 
    thread.start() 
    return HttpResponse("html response") 
+0

Właściwie, teraz, myślę o tobie może chcieć wywołać wątek = MyThread() w innej funkcji, abyś mógł uzyskać do niego dostęp i zatrzymać go później ... ale nadal możliwe rozwiązanie? – Ramalus

+0

Naprawdę nie rozumiem, jak twoja odpowiedź rozwiązuje cokolwiek. Po pierwsze, kod musi kończyć się *, zanim * zostanie zwrócona odpowiedź. Po drugie, nie pokazałeś nawet, w jaki sposób utworzony wątek zostanie automatycznie zatrzymany. –

+0

Och, masz rację, chyba nie myślałem o tym przez tak wiele. Przepraszam. – Ramalus

Powiązane problemy