2017-12-16 130 views
5

Chcę wykonać funkcję async za każdym razem, gdy jest wykonywana trasa kolby. Obecnie moja funkcja abar nie jest nigdy wykonywana. Czy możesz powiedzieć mi dlaczego? Dziękuję bardzo:Python3 Asyncio wywołanie z Flask route

import asyncio 
from flask import Flask 

async def abar(a): 
    print(a) 

loop = asyncio.get_event_loop() 
app = Flask(__name__) 

@app.route("/") 
def notify(): 
    asyncio.ensure_future(abar("abar"), loop=loop) 
    return "OK" 

if __name__ == "__main__": 
    app.run(debug=False, use_reloader=False) 
    loop.run_forever() 

Próbowałem ją również umieścić jeden blokowania połączeń w oddzielnym wątku. Ale wciąż nie wywołuje funkcji abar.

import asyncio 
from threading import Thread 
from flask import Flask 

async def abar(a): 
    print(a) 

app = Flask(__name__) 

def start_worker(loop): 
    asyncio.set_event_loop(loop) 
    try: 
     loop.run_forever() 
    finally: 
     loop.close() 

worker_loop = asyncio.new_event_loop() 
worker = Thread(target=start_worker, args=(worker_loop,)) 

@app.route("/") 
def notify(): 
    asyncio.ensure_future(abar("abar"), loop=worker_loop) 
    return "OK" 

if __name__ == "__main__": 
    worker.start() 
    app.run(debug=False, use_reloader=False) 
+2

'app.run' i' loop.run_forever' są zarówno blokujące. Prawdopodobnie lepiej jest użyć wątku. Jeśli chcesz użyć asyncio, powinieneś zajrzeć do jednego z frameworków podobnych do Flask. – dirn

+0

@dim Dziękuję bardzo. Próbowałem przenieść jedno blokowanie do osobnej nici. S. moje edytowane pytanie! – user24502

Odpowiedz

3

Dla tego samego powodu nie będzie widać ten druk:

if __name__ == "__main__": 
    app.run(debug=False, use_reloader=False) 
    print('Hey!') 
    loop.run_forever() 

loop.run_forever() nigdy nazywa ponieważ, jak już wspomniano app.run @dirn jest również blokowanie.

Uruchamianie globalnej pętli zdarzeń blokowania - to jedyny sposób na uruchomienie asyncio coroutines i zadań, ale nie jest ona kompatybilna z uruchomionym blokowaniem aplikacji Flask (lub ogólnie z jakąkolwiek inną rzeczą).

Jeśli chcesz korzystać z asynchronicznego środowiska sieciowego, powinieneś wybrać taki, który ma być asynchroniczny. Na przykład, prawdopodobnie najbardziej popularny teraz jest aiohttp:

from aiohttp import web 


async def hello(request): 
    return web.Response(text="Hello, world") 


if __name__ == "__main__": 
    app = web.Application() 
    app.router.add_get('/', hello) 
    web.run_app(app) # this runs asyncio event loop inside 

Upd:

O swojej próby uruchomienia pętli zdarzeń w wątku tła. Nie badałem zbyt wiele, ale wydaje się, że problem związany jest z bezpieczeństwem bieżnika: wiele obiektów asyncio nie jest bezpiecznych dla wątków. Jeśli zmienisz kod w ten sposób, zadziała:

def _create_task(): 
    asyncio.ensure_future(abar("abar"), loop=worker_loop) 

@app.route("/") 
def notify(): 
    worker_loop.call_soon_threadsafe(_create_task) 
    return "OK" 

Ale to bardzo zły pomysł. Jest to nie tylko bardzo niewygodne, ale wydaje mi się, że nie miałoby to większego sensu: jeśli zamierzasz używać wątku do uruchamiania asyncio, dlaczego nie just use threads in Flask zamiast asyncio? Będziesz mieć Flask, którego potrzebujesz i równoległość.

Jeśli nadal Cię nie przekonałem, przyjrzyj się przynajmniej projektowi Flask-aiohttp. Jest blisko Flask api i myślę, że jeszcze lepiej, co próbujesz zrobić.

+0

Dziękuję bardzo za wyjaśnienie. To ma sens. Również jego ładny mały przykład aiohttp. Niestety jestem związany z kolbą/kolbą - zapytaj o umiejętność alexa. Zmodyfikowałem swoje oryginalne pytanie i przeniosłem jedno połączenie blokujące w osobnym wątku. Ale wciąż nie ma szczęścia – user24502

+0

@ user24502 ​​Zaktualizowałem odpowiedź. –

2

Prostszym rozwiązaniem problemu (w moim stronniczym widoku) jest przełączenie na Quart z Flask. Jeśli więc fragment upraszcza się,

import asyncio 
from quart import Quart 

async def abar(a): 
    print(a) 

app = Quart(__name__) 

@app.route("/") 
async def notify(): 
    await abar("abar") 
    return "OK" 

if __name__ == "__main__": 
    app.run(debug=False) 

Jak zauważono w innych odpowiedzi aplikacja run Kolba blokuje i nie oddziałują z asyncio pętli. Quart z drugiej strony to API Flask zbudowane na asyncio, więc powinno działać tak, jak oczekujesz.

Również jako aktualizacja, Flask-Aiohttp nie jest już maintained.