2015-08-30 8 views
5

Sto provando a scrivere un'estensione per muffa che deve mantenere alcune informazioni tra le richieste. Funziona bene quando eseguo Werkzeug con un singolo processo, ma quando corro con più processi ottengo qualche comportamento strano che non capisco. Prendete questa semplice applicazione come esempio:Werkzeug e stato della classe con Flask: come vengono ripristinate le variabili dei membri della classe quando la classe non viene reinizializzata?

from flask import Flask 
app = Flask(__name__) 

class Counter(object): 
    def __init__(self, app): 
     print('initializing a Counter object') 
     self.app = app 
     self.value = 0 

    def increment(self): 
     self.value += 1 
     print('Just incremented, current value is ', self.value) 

counter = Counter(app) 

@app.route('/') 
def index(): 
    for i in range(4): 
     counter.increment() 
    return 'index' 

if __name__ == '__main__': 
    #scenario 1 - single process 
    #app.run() 
    #scenario 2 - threaded 
    #app.run(threaded=True) 
    #scenario 3 - two processes 
    app.run(processes=2) 

Per i primi due scenari si comporta esattamente come ci si aspetta: l'oggetto contatore viene inizializzato una volta e poi incrementa con ogni richiesta per il '/' percorso. Quando eseguo con il terzo scenario (processi di passaggio = 2), allora ottengo questo come output:

initializing a Counter object 
    * Running on http://127.0.0.1:5000/ (Press CTRL+C to quit) 
Just incremented, current value is 1 
Just incremented, current value is 2 
Just incremented, current value is 3 
Just incremented, current value is 4 
127.0.0.1 - - [30/Aug/2015 09:47:25] "GET/HTTP/1.1" 200 - 
Just incremented, current value is 1 
Just incremented, current value is 2 
Just incremented, current value is 3 
Just incremented, current value is 4 
127.0.0.1 - - [30/Aug/2015 09:47:26] "GET/HTTP/1.1" 200 - 
Just incremented, current value is 1 
Just incremented, current value is 2 
Just incremented, current value is 3 
Just incremented, current value is 4 
127.0.0.1 - - [30/Aug/2015 09:47:27] "GET/HTTP/1.1" 200 - 

Sembra che counter.value sta tornando al suo stato giusto dopo essere stato inizializzato senza essere re-inizializzato. Qualcuno potrebbe far luce su ciò che Werkzeug sta facendo internamente per fare in modo che ciò accada? Sarei anche molto interessato a imparare se c'è un modo per farlo comportarsi come mi aspetterei ingenuamente (due processi, ciascuno con la propria istanza di Counter). Grazie!

+0

hai trovato una soluzione a questo? Qualche dettaglio di implementazione? – user1658296

risposta

4

Il primo esempio (thread singolo) utilizza solo uno Counter, quindi funziona.

Il secondo esempio (più thread), i thread vengono generati per gestire ogni richiesta. Condividono la memoria con quella Counter che è stata creata prima della loro spawn, quindi incrementandoli da ciascuno aumenta la stessa cosa.

L'ultimo esempio (più processi), i processi vengono generati per gestire ogni richiesta. Flask's dev server utilizza fork: ogni bambino vede lo stesso punto di partenza (il contatore è già inizializzato) ma incrementa nel loro own address space che scompare quando termina la richiesta.

import os 

class Counter: 
    def __init__(self): 
     print('init') 
     self.value = 0 

    def increment(self): 
     self.value += 1 
     print('inc -> {}'.format(self.value)) 

counter = Counter() 

def multi(): 
    if not os.fork(): 
     # child starts with copy of parent memory 
     for _ in range(3): 
      # increments three times 
      counter.increment() 

     # child is done 
     os._exit(0) 

# three processes run 
for _ in range(3): 
    multi() 
init 
inc -> 1 
inc -> 2 
inc -> 3 
inc -> 1 
inc -> 2 
inc -> 3 
inc -> 1 
inc -> 2 
inc -> 3 

Utilizzare un database o altro supporto di memorizzazione esterno per archiviare stato globale attraverso i processi, utilizzando before_ e after_request. Si noti che non è esattamente semplice, poiché è necessario memorizzare il valore incrementato del contatore da ogni richiesta thread-safe, in modo che due thread non sovrascrivano il valore allo stesso tempo.

req 1 starts, gets stored value = 4 
req 2 starts, gets stored value = 4 
req 1 increments, value = 8 
req 1 saves, value = 8 
req 2 increments, value = 8 
req 2 saves, value = 8 but should = 12 
+0

@davidism: grazie, questo spiega il comportamento che non stavo capendo. Sembra che copiare tutta la memoria dall'applicazione per ogni singola richiesta sia più lento del semplice mantenimento di processi in esecuzione continua. Sai perché è fatto usando la biforcazione invece? – Ivanna

+0

@Ivanna Non ho idea delle menti degli sviluppatori, né se si tratti di prestazioni scadenti, ma eviterei di fare questo tipo di ipotesi. Questo è il modo in cui funziona il server di sviluppo ed è semplice da implementare. Dovresti comunque utilizzare un vero server in produzione, come uwsgi, che potrebbe farlo in modo diverso. – davidism

Problemi correlati