2013-01-17 1 views
49

Sono impegnato a scrivere un piccolo server di gioco per provare il pallone. Il gioco espone una API tramite REST agli utenti. È facile per gli utenti eseguire azioni e interrogare i dati, tuttavia mi piacerebbe servire il "mondo di gioco" al di fuori del ciclo app.run() per aggiornare le entità di gioco, ecc. Dato che Flask è implementato in modo così pulito, mi piacerebbe per vedere se c'è un modo Flask per farlo.Come posso aggiungere un thread in background alla beuta?

+0

Intendi qualcosa come Flask-Admin? Oppure, se stai utilizzando un ORM (SQL-Alchemy), puoi semplicemente creare una nuova sessione db per interrogare il database anche se l'applicazione è in esecuzione. – reptilicus

+0

Se è effettivamente necessario eseguire molti calcoli, è possibile utilizzare il modulo dei processi secondari e generare semplicemente nuovi processi per eseguire tale calcolo aggiuntivo. – Maus

+0

Questo è un piano, tuttavia il sottoprocesso manipolerà le strutture di dati, che vorresti accedere e impostare tramite l'api della beuta esposta. Non incontrerò problemi? – Marinus

risposta

43

I thread aggiuntivi devono essere avviati dalla stessa app richiamata dal server WSGI.

L'esempio seguente crea un thread in background che viene eseguito ogni 5 secondi e gestisce le strutture di dati che sono anche disponibili per le funzioni instradate di Flask.

import threading 
import atexit 
from flask import Flask 

POOL_TIME = 5 #Seconds 

# variables that are accessible from anywhere 
commonDataStruct = {} 
# lock to control access to variable 
dataLock = threading.Lock() 
# thread handler 
yourThread = threading.Thread() 

def create_app(): 
    app = Flask(__name__) 

    def interrupt(): 
     global yourThread 
     yourThread.cancel() 

    def doStuff(): 
     global commonDataStruct 
     global yourThread 
     with dataLock: 
     # Do your stuff with commonDataStruct Here 

     # Set the next thread to happen 
     yourThread = threading.Timer(POOL_TIME, doStuff,()) 
     yourThread.start() 

    def doStuffStart(): 
     # Do initialisation stuff here 
     global yourThread 
     # Create your thread 
     yourThread = threading.Timer(POOL_TIME, doStuff,()) 
     yourThread.start() 

    # Initiate 
    doStuffStart() 
    # When you kill Flask (SIGTERM), clear the trigger for the next thread 
    atexit.register(interrupt) 
    return app 

app = create_app()   

chiamare da Gunicorn con qualcosa di simile:

gunicorn -b 0.0.0.0:5000 --log-config log.conf --pid=app.pid myfile:app 
+6

Ho trovato che questo è problematico quando si utilizza la funzionalità di auto-ricarica di Flask (un nuovo thread è stato creato su ogni ricarica). Per risolvere il problema, ho usato [werkzeug.serving.is_running_from_reloader] (http://werkzeug.pocoo.org/docs/0.10/serving/#werkzeug.serving.is_running_from_reloader) per crearlo solo quando l'app non è in esecuzione dal reloader . – raffomania

+2

@caio dovrebbe essere "con dataLock:" capitale L sopra. –

+0

Questa è una bella soluzione; aiuta ad affrontare le applicazioni di flask che usano moduli di multiprocessing o threading. Mi piace. –

3

Sembra che ci sia a hackish way to do it, ma non penso che questo sia supportato tecnicamente.

Ho trovato anche this answer, che parla dell'utilizzo di boccetta di sedano per questo.

+1

+1 - sedano o qualche altro sistema di coda di attività è l'ideale per questo genere di cose - generalmente si ha meno controllo su thread o sotto processi (poiché il processo genitore può essere raccolto dal server senza preavviso). –

2

Oltre a utilizzare le discussioni puri o la coda di sedano (si noti che fiasco-sedano non è più necessario), si potrebbe anche avere una guardare pallone-apscheduler:

https://github.com/viniciuschiele/flask-apscheduler

Un semplice esempio copiato da https://github.com/viniciuschiele/flask-apscheduler/blob/master/examples/jobs.py:

from flask import Flask 
from flask_apscheduler import APScheduler 


class Config(object): 
    JOBS = [ 
     { 
      'id': 'job1', 
      'func': 'jobs:job1', 
      'args': (1, 2), 
      'trigger': 'interval', 
      'seconds': 10 
     } 
    ] 

    SCHEDULER_API_ENABLED = True 


def job1(a, b): 
    print(str(a) + ' ' + str(b)) 

if __name__ == '__main__': 
    app = Flask(__name__) 
    app.config.from_object(Config()) 

    scheduler = APScheduler() 
    # it is also possible to enable the API directly 
    # scheduler.api_enabled = True 
    scheduler.init_app(app) 
    scheduler.start() 

    app.run()