你是对的,在视图函数中进行长时间计算通常是不受欢迎的 - 我的意思是,如果它是一个典型的网站,其随机访问者能够将网络服务器线程挂起一分钟,那么它就是 DoS 漏洞的秘诀.但在某些情况下(内部网站、少数用户、只有管理员可以访问“上传 csv”表单),您可能会侥幸成功。事实上,我曾经有运行几个小时的维护脚本 :)
这里的诀窍是避免浏览器超时——此时您的客户端将数据发送到服务器并坐在那里等待任何回复,不知道他们的请求是否正在处理。通常,在大约 60 秒时,浏览器(或代理,或前端网络服务器)可能会变得不耐烦并关闭连接。然后,您的服务器进程将尝试将任何内容写入已关闭的连接并崩溃/引发错误。
为了防止这种情况发生,服务器需要定期向连接写入一些内容,以便客户端看到服务器处于活动状态并且不会关闭连接。
“普通”金字塔模板被缓冲 - 即在整个模板生成之前输出不会发送到客户端。因此,您需要直接使用response.app_iter / response.body_file 并在那里定期输出一些数据。
作为示例,您可以复制 Pyramid Cookbook 中的 Todo List Application in One File 示例,并将 new_view 函数替换为以下代码(其本身已从 this question 借用):
@view_config(route_name='new', request_method='GET', renderer='new.mako')
def new_view(request):
return {}
@view_config(route_name='new', request_method='POST')
def iter_test(request):
import time
if request.POST.get('name'):
request.db.execute(
'insert into tasks (name, closed) values (?, ?)',
[request.POST['name'], 0])
request.db.commit()
def test_iter():
i = 0
while True:
i += 1
if i == 5:
yield str('<p>Done! <a href="/">Click here</a> to see the results</p>')
raise StopIteration
yield str('<p>working %s...</p>' % i)
print time.time()
time.sleep(1)
return Response(app_iter=test_iter())
(当然,这个解决方案在 UI 方面并不太花哨,但你说你不想搞乱 websockets 和 celery)