我有一个Flask
网络应用,可能需要通过Sqlalchemy
给定用户输入执行一些繁重的SQL查询。我想为查询设置超时,让我们说20秒,因此如果查询超过20秒,服务器将向用户显示错误消息,以便他们可以稍后再尝试或使用较小的输入。 / p>
我已尝试使用Flask开发服务器和Gunicorn的multiprocessing
和threading
模块,但没有成功:服务器保持阻塞状态,并且不返回任何错误消息。您将在下面找到代码的摘录。
如何以用户友好的方式处理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)
答案 0 :(得分:5)
我建议使用Celery或类似的东西(人们使用python-rq进行简单的工作流程)。 看看有关Celery的Flask文档:http://flask.pocoo.org/docs/0.12/patterns/celery/
至于处理长时间运行查询的结果:您可以创建一个端点来请求任务结果,并让客户端应用程序定期检查此端点,直到结果可用。
答案 1 :(得分:3)
正如leovp提到的那样,如果你正在进行一项长期项目,Celery就是你要走的路。但是,如果您正在开发一个容易设置的小项目,我建议您使用RQ并flask 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%确定它没有错误。