首页 > 解决方案 > 在烧瓶应用程序中如何引入异步性?

问题描述

在我的烧瓶应用程序中,有一些 REST 端点需要花费太多时间来响应。调用时,它主要在数据库中执行一些 CRUD 操作,其中一些可以异步进行。如果它向客户端发送响应并且数据库插入在后台继续进行,我没有任何问题。我想用asyncio,但听说flask不支持asyncio。在那种情况下,我只剩下线程的选择。有什么建议么?我没有倾倒烧瓶的选项。我不想使用 Celery,因为它的变化太大了。

在某些地方使用线程时它可以工作,而在其他地方则不行。看起来它没有找到应用程序上下文。

RuntimeError: Working outside of application context.
This typically means that you attempted to use functionality that needed
to interface with the current application object in some way. To solve
this, set up an application context with app.app_context().  See the
documentation for more information.

在线程中,我的第一行代码是 - 它在这里失败:

user: AuthUser = g.logged_in_user

@Edit在视图函数中,我必须做多件事-其中一些是链接的,因此不能异步-它们需要按顺序发生,因为从一个数据库调用返回的结果用于调用下一个调用和最终结果用于编写方法返回的 json 输出。只有一个数据库调用独立于其他数据库调用,插入@ 1K 记录是减慢 api 响应的最大因素。

如果我把这个方法放在最后,它说 psycopg2 连接已经关闭 - 尽管我从未明确关闭连接 - 可能只是视图函数返回了 json 有效负载。

如果我将重方法放在视图函数的开头,它就可以工作。

@Edit2 从视图函数我传递应用程序上下文和用户。我没有将数据库连接传递给线程,而是从线程内部连接到 PostgreSql 数据库。

threading.Thread(target=set_responder_contacts,
                         args=(user, template_id,),
                         kwargs={'app':current_app._get_current_object()})
                 .start()

线程调用的函数的代码片段:

def set_responder_contacts(user: AuthUser, template_id: int, app=None):
    with app.app_context():
        try:
            db = dbConn()
            #database insertion code

标签: python-3.xflaskpython-asynciopsycopg2

解决方案


asyncio不会直接在这里提供帮助。

如果您相信您的服务器进程在后台功能期间保持运行,那么当然,只需启动一个线程并在那里进行后台工作:

def heavy_work(some_id):
    pass


@app.post(...)
def view(...):
    some_id = create_thing(...)
    threading.Thread(target=heavy_work, args=(some_id,)).start()
    return "Okay (though processing in the background)"

有一些警告:

  • 正如我之前提到的,如果 WSGI 服务器进程由于某种原因被终止(例如,超出内存或请求计数限制,或者它彻底崩溃),后台操作将被执行。
  • 如果繁重的操作严重依赖 CPU,它可能会影响同一服务器进程所服务的其他请求的性能。

推荐阅读