2013-04-27 8 views
7

Qualcosa a cui ho appena pensato:Cosa succede quando hai un ciclo infinito nel codice della vista Django?

Dire che sto scrivendo il codice di visualizzazione per il mio sito Django, e faccio un errore e creo un ciclo infinito.

Ogni volta che qualcuno tenta di accedere alla vista, il worker assegnato alla richiesta (sia esso un Gevent worker o un thread Python) resterebbe in un ciclo indefinito.

Se ho capito bene, il server avrebbe inviato un errore di timeout al client dopo 30 secondi. Ma cosa succederà con l'operatore Python? Continuerà a lavorare indefinitamente? Sembra pericoloso!

Immagina di avere un server in cui sono stati assegnati 10 dipendenti. Lascio correre e ad un certo punto, un client tenta di accedere alla vista con il ciclo infinito. A quest'ultimo verrà assegnato un worker e sarà effettivamente morto fino al successivo riavvio del server. La cosa pericolosa è che inizialmente non lo noterei, perché il sito sarebbe semplicemente impercettibilmente più lento, con 9 operai invece di 10. Ma poi potrebbe succedere ancora e ancora per un lungo lasso di tempo, forse mesi. Il sito diventerebbe progressivamente più lento, finché alla fine sarebbe davvero lento con un solo lavoratore.

Un riavvio del server risolverebbe il problema, ma non mi piacerebbe che la funzionalità del mio sito dipenda dal riavvio del server.

Si tratta di un problema reale? C'è un modo per evitarlo?

Aggiornamento: Mi piacerebbe anche davvero apprezzare un modo per prendere uno stacktrace del filo/lavoratore che è bloccato in un loop infinito, così ho potuto avere quel inviato via email a me così io sarò a conoscenza del problema . (Non so come farlo perché non viene sollevata alcuna eccezione.)

Aggiornamento alle persone che dicono le cose all'effetto di "Evita di scrivere codice che ha loop infiniti": Nel caso in cui non fosse ovvio, non passo il mio tempo libero intenzionalmente a inserire loop infiniti nel mio codice. Quando accadono queste cose, sono errori e gli errori possono essere minimizzati ma mai completamente evitati. Voglio sapere che anche quando commetto un errore, ci sarà una rete di sicurezza che mi avviserà e mi consentirà di risolvere il problema.

+2

lettura interessante: http: // StackOverflow. it/questions/8685695/how-do-i-run-lungo-infinito-python-process –

+0

Ho aggiornato la mia risposta, spero che risponda alla tua domanda ora :) –

risposta

4

Si tratta di un problema reale. In caso di gevent, a causa del cambio di contesto, può anche interrompere immediatamente la risposta del tuo sito web.

Tutto dipende dal proprio ambiente. Ad esempio, durante l'esecuzione di django in produzione tramite uwsgi è possibile impostare harakiri, ovvero il tempo in secondi, dopodiché il thread che gestisce la richiesta verrà ucciso se non ha terminato la gestione della risposta. Si consiglia vivamente di impostare tale valore al fine di gestire alcune richieste difettose o codice errato. Tale evento è riportato nel registro uwsgi. Credo che altre soluzioni per l'esecuzione di Django in produzione abbiano opzioni simili.

In caso contrario, a causa dell'architettura di rete, la disconnessione del client non interromperà il ciclo infinito, e per impostazione predefinita non ci sarà alcuna risposta - solo un caricamento infinito. Varie opzioni di timeout (una delle quali è harakiri) possono terminare con il timeout della connessione - ad esempio, php ha (per quanto ricordo) un timeout predefinito di 30 secondi e restituirà il timeout del gateway 504. Il timeout di disconnessione socket dipende dalle impostazioni del server http e non interromperà il thread dell'applicazione, ma chiuderà solo il socket client.

Se non si utilizza gevent (o qualsiasi altro thread verde), il ciclo infinito tenderà a assorbire il 100% della potenza della CPU disponibile (limitata a un core), probabilmente consumando sempre più memoria, quindi il sito Web funzionerà in modo soddisfacente lento e/o timeout molto veloce. Django non è a conoscenza del tempo richiesto, quindi - come detto prima - lo stack dell'ambiente di produzione è il modo per evitare che ciò accada. In caso di uwsgi, http://uwsgi-docs.readthedocs.org/en/latest/Options.html#harakiri-verbose è la strada da percorrere.

Harakiri fa analisi dello stack di stampa dei proces uccisi: (https://uwsgi-docs.readthedocs.org/en/latest/Tracebacker.html?highlight=harakiri) direttamente al uwsgi log, e grazie al sistema di allarme è possibile ottenere una notifica tramite e-mail (http://uwsgi-docs.readthedocs.org/en/latest/AlarmSubsystem.html)

+0

L'opzione Harakiri è un passo nella giusta direzione, perché impedisce al server di rimanere bloccato, ma non aiuta a trovare la radice del problema e a risolverlo. Quello che vorrei è avere una stacktrace dell'operaio incriminato che mi ha inviato per email in modo da poterlo ispezionare e correggere il problema nel codice. –

+0

Harakiri stampa traccia dello stack e richiede informazioni e il sistema di allarme nginx consente la notifica via e-mail. Risposta aggiornata con link –

+0

Scusate, volevo dire sistema di allarme uwsgi ovviamente :) –

0

Sì, l'analisi è corretta. Il thread/processo di lavoro continuerà a essere in esecuzione. Inoltre, se non ci sono wait/sleep nel loop, si accosterà la CPU. Altri thread/processo otterranno pochissima CPU, con conseguente rallentamento dell'intero sito.

Inoltre, non penso che il server invierà esplicitamente un errore di timeout al client. Se è impostato il timeout TCP, la connessione TCP verrà chiusa.

Il client può anche disporre di alcune impostazioni di timeout per ottenere una risposta, che potrebbe risultare in un'immagine.

Evitare tale codice è il modo migliore per evitare tale codice. È anche possibile avere uno strumento di monitoraggio sul server per cercare l'utilizzo della CPU/memoria e notificare attività anomale in modo da poter agire.

2

Ho appena provato questo sul server di sviluppo di Django.

Risultati:

  • non dà un timeout dopo 30 secondi.(questo potrebbe anche perché non è un server di produzione)
  • Rimane in caricamento finché non chiudo la pagina.

Immagino che un modo per evitarlo, senza in realtà evitare solo un codice del genere, sarebbe utilizzare il threading per avere il controllo dei timeout e poter interrompere il thread.

Forse qualcosa di simile:

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

In realtà, ora che ti penso, tu potrebbe voler chiamare il thread = MyThread() in un'altra funzione in modo da poter effettivamente accedervi e fermarlo in seguito .. ma ancora una possibile soluzione? – Ramalus

+0

Davvero non capisco come la tua risposta risolva nulla. Per prima cosa, il codice deve finire * prima * la risposta viene restituita. In secondo luogo, non hai nemmeno mostrato come il thread che hai creato verrà automaticamente arrestato. –

+0

Oh, hai ragione, credo di non averlo pensato così tanto. Mi dispiace. – Ramalus

Problemi correlati