2013-01-17 14 views
49

Jestem zajęty pisaniem małego serwera gry, aby wypróbować flakon. Gra udostępnia API użytkownikom poprzez REST. Łatwo jest użytkownikom wykonywać akcje i dane o zapytaniach, jednak chciałbym obsługiwać "świat gry" poza pętlą app.run(), aby zaktualizować elementy gry itp. Biorąc pod uwagę, że Flask jest tak czysto zaimplementowany, chciałbym aby sprawdzić, czy istnieje sposób Flask do tego.Jak dodać wątek tła do kolby?

+0

Masz na myśli coś takiego jak Flask-Admin? Lub jeśli używasz ORM (alchemia SQL), możesz po prostu utworzyć nową sesję db, aby wysłać zapytanie do bazy danych, nawet jeśli aplikacja jest uruchomiona. – reptilicus

+0

Jeśli rzeczywiście potrzebujesz dużo obliczeń, możesz użyć modułu podprocesu i po prostu odradzać nowe procesy, aby wykonać dodatkowe obliczenia. – Maus

+0

To jest plan, jednak proces podrzędny będzie manipulował strukturami danych, do których chcesz uzyskać dostęp i ustawić za pomocą api wyeksponowanego kolby. Czy nie napotkasz problemów? – Marinus

Odpowiedz

43

Twoje dodatkowe wątki muszą być inicjowane z tej samej aplikacji, która jest wywoływana przez serwer WSGI.

Poniższy przykład tworzy wątek tła, który wykonuje co 5 sekund i manipuluje strukturami danych, które są również dostępne dla funkcji kierowanych 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()   

zadzwonić z Gunicorn coś takiego:

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

Stwierdziłem, że jest to problematyczne przy korzystaniu z funkcji automatycznego przeładowywania skrzynki (nowy wątek został utworzony przy każdym ponownym załadowaniu). Aby to naprawić, użyłem [werkzeug.serving.is_running_from_reloader] (http://werkzeug.pocoo.org/docs/0.10/serving/#werkzeug.serving.is_running_from_reloader), aby utworzyć go tylko wtedy, gdy aplikacja nie działa z reloadera . – raffomania

+2

@caio powinno być "z dataLock:" kapitału L powyżej. –

+0

To jest dobre rozwiązanie; pomaga radzić sobie z aplikacjami do kolb, które używają modułów wieloprocesowych lub wątków. Lubię to. –

3

Wygląda na to, że istnieje a hackish way to do it, ale nie sądzę, że jest to technicznie obsługiwane.

Znalazłem również this answer, który mówi o użyciu do tego celu selera-kolby.

+1

+1 - seler lub inny system kolejki zadań jest idealny dla tego rodzaju rzeczy - generalnie masz mniej kontroli nad wątkami lub podprocesami (ponieważ proces macierzysty może być zbierany przez serwer bez powiadomienia). –

2

Oprócz korzystania z czystych nici lub kolejkę Seler (zauważ, że skrzynkowego seler nie jest wymagane), można też mieć spojrzeć skrzynkowego apscheduler:

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

prostym przykładem kopiowane 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() 
Powiązane problemy