Flask 的异步调用方法

Posted

技术标签:

【中文标题】Flask 的异步调用方法【英文标题】:Async call method with Flask 【发布时间】: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,但它似乎只对一个功能有点矫枉过正。

【问题讨论】:

【参考方案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 并使用不包含 asyncawaitspawn(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

【讨论】:

以上是关于Flask 的异步调用方法的主要内容,如果未能解决你的问题,请参考以下文章

python使用flask+Thread实现异步任务

来自异步芹菜工作者的 SocketIO 发出不工作

调用路由方法时,在Flask对象之前使用'@'有啥需要[重复]

java 异步调用方法

java 异步调用方法

使用 Flask 从 API 调用端点的正确方法