【问题标题】:Async call method with FlaskFlask 的异步调用方法
【发布时间】:2018-06-20 07:18:10
【问题描述】:

我正在尝试通过 Flask 方法调用阻塞函数,但它需要几秒钟,所以我想我可以做一些异步调用来加快速度,但它没有按预期工作。 显然使用 asyncio 我不能只在后台启动协程而不等待执行结束,也许我需要使用线程?或者使用 grequest 作为我的阻塞函数正在使用请求...

到目前为止,这是我的代码:

@app.route("/ressource", methods=["GET"])
def get_ressource():
    do_stuff()
    return make_response("OK",200)  

def do_stuff():
  # Some stuff
  fetch_ressource()


async def fetch_ressource():
    return await blocking_function()


def blocking_function():
  # Take 2-3 seconds
  result = request.get('path/to/remote/ressource')
  put_in_database(result)

我听说过 Celeri,但它似乎只对一个功能有点矫枉过正。

【问题讨论】:

    标签: python-3.x flask python-asyncio


    【解决方案1】:

    现在回答有点晚了,但我对此很感兴趣。

    我通过包装函数并通过asyncio.run()调用它来管理它,但我不知道多个asyncio.run()调用是否是一件好事。

    from functools import wraps
    from flask import Flask
    import asyncio
    
    def async_action(f):
        @wraps(f)
        def wrapped(*args, **kwargs):
            return asyncio.run(f(*args, **kwargs))
        return wrapped
    
    app = Flask(__name__)
    
    @app.route('/')
    @async_action
    async def index():
        await asyncio.sleep(2)
        return 'Hello world !'
    
    app.run()
    

    【讨论】:

    • 这将禁用协程的所有优点。如果说有两个请求调用索引页,flask 会处理第二个请求,直到第一个请求完成,在正确使用协程的情况下,我们可以同时处理第二个请求和第一个请求。
    【解决方案2】:

    您可以使用 Quart 和 AIOHTTP 执行此操作,代码应该非常熟悉给定的 Flask 代码,

    @app.route("/ressource", methods=["POST"])
    async def get_ressource():
        asyncio.ensure_future(blocking_function())
        return await make_response("OK", 202)  
    
    async def blocking_function():
        async with aiohttp.ClientSession() as session:
            async with session.get('path/to/remote/ressource') as resp:
                result = await resp.text()
        await put_in_database(result)
    

    注意:我已将其更改为 POST 路由,因为它正在执行某些操作,并且我已返回 202 响应以表明它已触发处理。

    如果您希望坚持使用 Flask,我建议您使用 eventlet 并使用不包含 async 或 await 的 spawn(blocking_function)。

    还要注意我是 Quart 作者。

    【讨论】:

    • 是的,这通常是一个帖子,我在工作的 sn-p 中误用了一个 get。我有点害怕只为一个函数更改我的所有框架(即使它似乎几乎是相同的语法),我要试试 eventlet!
    • 在烧瓶进程中执行异步代码不是很糟糕吗?
    【解决方案3】:

    阻塞函数在做什么?

    使用 grequest 可行吗?

    import grequests
    
    @app.route('/do', methods = ['POST'])
    def do():
        result = grequests.map([grequests.get('slow api')])
        return result[0].content
    

    【讨论】:

    • 我的阻塞函数通过请求获取一些资源(所以是的,可以在这里使用 grequest),然后将其保存在数据库中,如果我使用 grequest,db 中的所有保存也将是异步的?
    • 这里的问题是我的客户端必须等待慢速api的响应才能有响应。
    • 我想没有多少解决方案...如果前进的唯一可能性是从缓慢的 API 中获得答案... API 是罪魁祸首。你可以在上面加糖(可爱的加载图像,类似的 UX/UI 魔法)但是......最后你会等待,对吧?如果您不想使用像 Celery 这样更复杂的其他解决方案,我会尝试 grequests。测试会很快,你会明白这条路是否适合你。
    【解决方案4】:

    考虑使用 Sanic 框架,它的设计考虑到了异步操作,你可以看到 sanic 版本的代码here

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2020-12-19
      • 1970-01-01
      • 2015-11-28
      • 2020-01-17
      • 1970-01-01
      相关资源
      最近更新 更多