如何在Flask中处理长SQL查询?

时间:2017-03-14 15:40:13

标签: python mysql web-applications flask sqlalchemy

我有一个Flask网络应用,可能需要通过Sqlalchemy给定用户输入执行一些繁重的SQL查询。我想为查询设置超时,让我们说20秒,因此如果查询超过20秒,服务器将向用户显示错误消息,以便他们可以稍后再尝试或使用较小的输入。 / p>

我已尝试使用Flask开发服务器和Gunicorn的multiprocessingthreading模块,但没有成功:服务器保持阻塞状态,并且不返回任何错误消息。您将在下面找到代码的摘录。

如何以用户友好的方式处理Flask中的慢速SQL查询?

感谢。

from multiprocessing import Process

@app.route("/long_query")
def long_query():
    query = db.session(User)

    def run_query():
        nonlocal query
        query = query.all()

    p = Process(target=run_query)
    p.start()

    p.join(20) # timeout of 20 seconds
    if p.is_alive():
        p.terminate()
        return render_template("error.html", message="please try with smaller input")

    return render_template("result.html", data=query)

2 个答案:

答案 0 :(得分:5)

我建议使用Celery或类似的东西(人们使用python-rq进行简单的工作流程)。 看看有关Celery的Flask文档:http://flask.pocoo.org/docs/0.12/patterns/celery/

至于处理长时间运行查询的结果:您可以创建一个端点来请求任务结果,并让客户端应用程序定期检查此端点,直到结果可用。

答案 1 :(得分:3)

正如leovp提到的那样,如果你正在进行一项长期项目,Celery就是你要走的路。但是,如果您正在开发一个容易设置的小项目,我建议您使用RQflask plugin。还要非常认真地考虑在查询数据库时终止进程,因为他们可能无法自行清理(例如释放他们在数据库上的锁)

如果您真的想在超时时终止查询,我建议您使用支持它的数据库(PostgreSQL就是其中之一)。我假设您在本节中使用PostgreSQL。

from sqlalchemy.interfaces import ConnectionProxy

class ConnectionProxyWithTimeouts(ConnectionProxy):

    def cursor_execute(self, execute, cursor, statement, parameters, context, executemany):

        timeout = context.execution_options.get('timeout', None)

        if timeout:
            c = cursor._parent.cursor()
            c.execute('SET statement_timeout TO %d;' % int(timeout * 1000))
            c.close()

            ret = execute(cursor, statement, parameters, context)

            c = cursor._parent.cursor()
            c.execute('SET statement_timeout TO 0')
            c.close()
            return ret
        else:
            return execute(cursor, statement, parameters, context)

然后,当您创建引擎时,您将拥有自己的连接代理

engine = create_engine(URL, proxy=TimeOutProxy(), pool_size=1, max_overflow=0)

你可以像这样查询

 User.query.execution_options(timeout=20).all()

如果您想使用上面的代码,请仅将其用作您自己实现的基础,因为我并非100%确定它没有错误。