2015-06-17 14 views
6

Ho utilizzato il pallone e alcuni dei miei gestori di percorso hanno iniziato i calcoli che possono richiedere diversi minuti. Usando il server di sviluppo di Flask, posso usare app.run (threaded = True) e il mio server continuerà a rispondere ad altre richieste mentre è spento eseguendo questi calcoli di più minuti.threading = True con il boccaglio

Ora ho iniziato a utilizzare Flask-SocketIO e non sono sicuro di come fare la cosa equivalente. Capisco che posso generare in modo esplicito un thread separato in python ogni volta che inizia uno di questi calcoli. È l'unico modo per farlo? O c'è qualcosa di equivalente a threaded = True per flask-socketio. (O, più probabilmente, sono solo completamente confuso.)

Grazie per qualsiasi aiuto.

risposta

13

L'idea della modalità thread in Flask/Werkzeug è di consentire al server di sviluppo di gestire più richieste contemporaneamente. Nella modalità predefinita, il server può gestire una richiesta alla volta, se un client invia una richiesta mentre il server sta già elaborando una richiesta precedente, quindi la seconda richiesta deve attendere fino al completamento della prima richiesta. In modalità threaded, Werkzeug genera un thread per ogni richiesta in entrata, quindi più richieste vengono gestite contemporaneamente. Ovviamente stai sfruttando la modalità thread per avere richieste che impiegano molto tempo a tornare, mantenendo il server reattivo alle altre richieste.

Si noti che questo approccio è difficile da scalare correttamente quando si esce dal server di sviluppo Web e in un server Web di produzione. Per un server basato sul lavoratore devi scegliere un numero fisso di lavoratori e questo ti dà il numero massimo di richieste simultanee che puoi avere.

L'approccio alternativo consiste nell'utilizzare un server basato su coroutine, come gevent, che è completamente supportato da Flask. Per gevent esiste un singolo processo di lavoro, ma al suo interno sono presenti più thread leggeri (o "verdi") che consentono l'esecuzione reciproca in modo cooperativo. La chiave per far funzionare le cose con questo modello è assicurarsi che questi thread verdi non abusino del tempo della CPU che ottengono, perché solo uno può essere eseguito alla volta. Quando ciò viene fatto correttamente, il server può scalare molto meglio rispetto all'approccio multiproprietà descritto sopra e si può facilmente avere centinaia/migliaia di client gestiti in questo modo.

Quindi ora si desidera utilizzare Flask-SocketIO e questa estensione richiede l'utilizzo di gevent. Nel caso in cui la ragione di questo requisito non sia chiara, diversamente dalle richieste HTTP, SocketIO utilizza il protocollo WebSocket, che richiede connessioni a lunga durata. L'utilizzo di thread gevent e green consente di avere un numero potenzialmente elevato di client costantemente connessi, cosa che sarebbe impossibile fare con più lavoratori.

Il problema è il tuo calcolo lungo, che non è amichevole per il tipo di server gevent. Per farlo funzionare, è necessario assicurarsi che la funzione di calcolo restituisca spesso, in modo che altri thread abbiano la possibilità di essere eseguiti e di non morire di fame. Ad esempio, se la funzione di calcolo ha un ciclo in dentro, si può fare qualcosa di simile:

def my_long_calculation(): 
    while some_condition: 
     # do some work here 

     # let other threads run 
     gevent.sleep() 

La funzione sleep() sarà essenzialmente fermare il tuo thread e passare a qualsiasi altro thread che hanno bisogno di CPU. Alla fine il controllo sarà restituito alla tua funzione, e a quel punto passerà alla successiva iterazione. È necessario assicurarsi che le chiamate di sonno non siano troppo distanziate (in questo modo il resto dell'applicazione non risponde) o non troppo vicino (in quanto ciò potrebbe rallentare il calcolo).

Quindi per rispondere alla tua domanda, purché tu citi correttamente nel tuo lungo calcolo, non devi fare nulla di speciale per gestire richieste concorrenti, poiché questa è la normale modalità operativa di gevent.

Se per qualsiasi motivo l'approccio di rendimento non è possibile, potrebbe essere necessario pensare di scaricare le attività ad alta intensità della CPU in un altro processo. Forse usa Celery per fare in modo che siano fatti come una coda di lavoro.

Ci scusiamo per la risposta lunga e tortuosa. Spero che questo ti aiuti!

+0

Grazie mille per questa risposta Miguel. (Ti avrei ringraziato prima, ma ero in vacanza.) Questo è estremamente chiaro e utile. –