我已经启动并运行了一个简单的金字塔应用程序,大多数视图都是一个围绕sqlite数据库的相当薄的包装器,其中包含用于编辑/添加一些信息的表单。
每月几次需要将新的数据块添加到此系统中(通过csv import)。数据保存在SQL表中(整个过程直到提交大约需要4秒)。
每次上传新的数据块时,都会触发重新计算数据库中的其他表。重新计算过程需要相当长的时间(一个月的数据大约需要21-50秒)。
目前我只是让浏览器/客户端等待流程完成,但我确实预计计算过程会花费越来越多的时间,因为系统会获得更多的使用。从UI的角度来看,这显然看起来像一个挂起的过程。
我该怎么做才能向用户表明: -
漫长的等待是正常/预期的?
他们应该等多久(进度条等)?
注意:我不是在这里询问长轮询或websockets,因为这不是一个真正的交互式应用程序,基于我的基本知识,websockets / async对于我的目的来说是过度的。
我想这是一个后续问题,我在视图函数中运行进程时做错了吗?几乎没有看到在网络上的示例/教程中完成。我应该在这种情况下使用芹菜或类似物吗?
答案 0 :(得分:1)
你是对的,在视图函数中进行长时间的计算通常是不受欢迎的 - 我的意思是,如果它是一个典型的网站,随机访问者能够挂起一个网络服务器线程一分钟,那么这是一个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())
(对于cource来说,这个解决方案在UI方面并不太花哨,但是你说你不想弄乱webockets和芹菜)
答案 1 :(得分:0)
浏览器操作触发的长时间运行流程是什么?即,用户正在上传已处理的CSV,然后视图正在进行处理吗?对于运行浏览器进程的简短,我通过jQuery或javascript使用了一个加载指示器,基本上在进程运行时弹出一个模态动画微调器,然后当它完成隐藏微调器时。
但是如果你进入越来越长的进程,我认为你应该真正看一下将从UI卸载的某种后台处理。它不必是基于消息的工作者,但即使是最终用户上传文件,并且在数据库中设置“待处理”条目。然后,您可以在后台定期安排pyramid script轮询状态表并运行它找到的任何内容。您可以将视图中的文件处理移动到单独的方法,并且可以从命令行脚本调用该方法。然后,当处理完成时,它可以更新状态表,指示它已完成,并且该反馈可以在某处呈现给用户,并且不会一直阻止它们的UI。