flaskルートが実行されるたびに非同期関数を実行したい。現在、私のabar関数は実行されません。私に理由を教えてくれる?どうもありがとうございました:
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()
また、1つのブロッキング呼び出しを別のスレッドに入れようとしました。しかし、まだ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)
一部の非同期機能をFlaskアプリに完全にasyncioに変換することなく組み込むことができます。
import asyncio
from flask import Flask
async def abar(a):
print(a)
loop = asyncio.get_event_loop()
app = Flask(__name__)
@app.route("/")
def notify():
loop.run_until_complete(abar("abar"))
return "OK"
if __name__ == "__main__":
app.run(debug=False, use_reloader=False)
これにより、非同期関数が戻るまでFlask応答がブロックされますが、それでもいくつかの巧妙なことができます。このパターンを使用して、 aiohttp を使用して多数の外部リクエストを並行して実行し、それらが完了すると、データ処理とテンプレートレンダリングの従来のflaskに戻ります。
import aiohttp
import asyncio
import async_timeout
from flask import Flask
loop = asyncio.get_event_loop()
app = Flask(__name__)
async def fetch(url):
async with aiohttp.ClientSession() as session, async_timeout.timeout(10):
async with session.get(url) as response:
return await response.text()
def fight(responses):
return "Why can't we all just get along?"
@app.route("/")
def index():
# perform multiple async requests concurrently
responses = loop.run_until_complete(asyncio.gather(
fetch("https://google.com/"),
fetch("https://bing.com/"),
fetch("https://duckduckgo.com"),
fetch("http://www.dogpile.com"),
))
# do something with the results
return fight(responses)
if __name__ == "__main__":
app.run(debug=False, use_reloader=False)
あなたの問題に対するより簡単な解決策は(私の偏見では)Flaskから Quart に切り替えることです。その場合、スニペットは単純化され、
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)
他の回答で述べたように、Flaskアプリの実行はブロックされており、asyncioループと相互作用しません。一方、Quartはasyncio上に構築されたFlask APIであるため、期待どおりに機能するはずです。
また、更新として、Flask-Aiohttpは maintained ではなくなりました。
同じ理由で、この印刷物は表示されません。
if __name__ == "__main__":
app.run(debug=False, use_reloader=False)
print('Hey!')
loop.run_forever()
@dirnが既に述べたようにapp.run
もブロックしているため、loop.run_forever()
は呼び出されません。
グローバルブロッキングイベントループの実行-asyncio
コルーチンおよびタスクを実行できる唯一の方法ですが、ブロッキングFlaskアプリ(または他の一般的なもの)の実行とは互換性がありません。
非同期Webフレームワークを使用する場合は、非同期に作成されたものを選択する必要があります。たとえば、おそらく現在最も人気のあるのは 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
更新:
バックグラウンドスレッドでイベントループを実行しようとしています。私はあまり調査しませんでしたが、トレッドの安全性に何らかの問題があるようです。多くのasyncioオブジェクトはスレッドセーフではありません。この方法でコードを変更すると、動作します:
def _create_task():
asyncio.ensure_future(abar("abar"), loop=worker_loop)
@app.route("/")
def notify():
worker_loop.call_soon_threadsafe(_create_task)
return "OK"
しかし、これも非常に悪い考えです。非常に不便なだけでなく、asyncioを起動するためにスレッドを使用する場合、なぜasyncioの代わりに Flaskでスレッドを使用する しないのでしょうか?必要なFlaskと並列化があります。
それでも納得できなかったら、少なくとも Flask-aiohttp プロジェクトを見てください。 Flask apiに近いので、あなたがやろうとしていることよりもさらに良いと思います。
動作していなかった上記の動作コードを提供してくれたJL Diaz(RealPythonから)に感謝します。
ここで何かを変更する必要がある場合は、お気軽にコメントしてください。
import aiohttp
import asyncio
import async_timeout
from quart import Quart, jsonify
app = Quart(__name__)
async def fetch(url):
async with aiohttp.ClientSession() as session, async_timeout.timeout(10):
async with session.get(url) as response:
return await response.text()
def fight(responses):
return jsonify([len(r) for r in responses])
@app.route("/")
async def index():
# perform multiple async requests concurrently
responses = await asyncio.gather(
fetch("https://google.com/"),
fetch("https://bing.com/"),
fetch("https://duckduckgo.com"),
fetch("http://www.dogpile.com"),
)
# do something with the results
return fight(responses)
if __name__ == "__main__":
app.run(debug=False, use_reloader=False)