首页 文章

来自Flask路线的Python3 Asyncio调用

提问于
浏览
18

我想在每次执行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()

我也尝试将一个阻塞调用放在一个单独的线程中 . 但它仍然没有称之为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)

3 回答

  • 4

    您可以将一些异步功能合并到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并行执行许多外部请求,然后当它们完成时,我又回到传统的瓶子中进行数据处理和模板渲染 .

    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)
    
  • 17

    一个更简单的问题解决方案(在我的偏见视图中)是从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 app运行是阻塞的,并且不与asyncio循环交互 . 另一方面,Quart是基于asyncio构建的Flask API,所以它应该如你所愿 .

    另外,作为更新,Flask-Aiohttp不再是maintained .

  • 10

    出于同样的原因,你不会看到这个印刷品:

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

    永远不会调用 loop.run_forever() ,因为@dirn已经注意到 app.run 也在阻塞 .

    运行全局阻塞事件循环 - 只能运行 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
    

    Upd:

    关于尝试在后台线程中运行事件循环 . 我没有多少调查,但似乎问题与踏板安全有关:许多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"
    

    但同样,这是一个非常糟糕的主意 . 它有道理:如果你're going to use thread to start asyncio, why don' t just use threads in Flask而不是asyncio?您将拥有所需的Flask和并行化 .

    如果我仍然没有说服你,至少看看Flask-aiohttp项目 . 它接近Flask api,我认为你想要做的更好 .

相关问题