【问题标题】:How to show a 'processing' or 'in progress' view while pyramid is running a process?如何在金字塔运行进程时显示“处理中”或“进行中”视图?
【发布时间】:2016-06-13 09:20:22
【问题描述】:

我已经启动并运行了一个简单的金字塔应用程序,大多数视图都是一个相当薄的 sqlite 数据库包装,其中包含用于编辑/添加一些信息的表单。

每月有几次需要将新数据块添加到该系统(通过 csv 导入)。数据保存在 SQL 表中(直到提交的整个过程大约需要 4 秒)。

每次上传新数据块时,都会触发数据库中其他表的重新计算。重新计算过程需要相当长的时间(一个月的数据大约需要 21-50 秒)。

目前我只是让浏览器/客户端坐在那里等待过程完成,但我确实预见到随着系统的使用越来越多,计算过程会花费越来越多的时间。从 UI 的角度来看,这显然是一个挂起的进程。

我可以做些什么来向用户表明:-

  1. 漫长的等待是正常/预期的吗?

  2. 他们应该等待多长时间(进度条等)?

注意:我不是在这里询问长轮询或 websockets,因为这不是一个真正的交互式应用程序,并且根据我的基本知识,websockets/async 对于我的目的来说是多余的。

我想此时有一个后续问题,我在视图函数中运行进程是否做错了?似乎很难在网络上的示例/教程中看到这样做。在这种情况下我应该使用芹菜或类似的东西吗?

【问题讨论】:

    标签: python pyramid


    【解决方案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())
    

    (当然,这个解决方案在 UI 方面并不太花哨,但你说你不想搞乱 websockets 和 celery)

    【讨论】:

    • 我对这个问题很感兴趣,并尝试了你的答案。输出一次全部呈现(全部 5 行)。那是对的吗?我认为它会一次显示一行“工作”输出,直到完成。
    • @fatfantasma:这可能是由于网络服务器(女服务员和/或前端网络服务器,如 Nginx)也在缓冲输出 - 请参阅此答案:stackoverflow.com/a/21540659/320021 我已经用Pyramid 的旧设置使用 PasteDeploy 并且每秒打印一行。
    • @fatfantasma:作为一个有趣的实验,您可以尝试编写大量空格来强制网络服务器在每行之后刷新缓冲区。浏览器输出看起来相同,因为空格会被折叠。不确定我会在生产中尝试这个,但这样的事情会起作用:yield str('

      working %s...

      %s' % (i, ' ' * 20000))
    • 我试过你的冲洗字符串,但也没有用。一切都在最后呈现。我现在正在尝试查看女服务员文档以尝试配置 send_bytes = 1
    • 我用 send_bytes =1 配置了女服务员。输出现在一次呈现 1 行。我很高兴我让实验开始工作,但是这个 SO 问题表明它会减慢一切,所以它并不值得。我今天确实学到了一些东西。谢谢:)
    【解决方案2】:

    那么长时间运行的进程是由浏览器操作触发的吗?即,用户正在上传已处理的 CSV,然后视图正在那里进行处理?对于短时间运行的浏览器进程,我通过 jQuery 或 javascript 使用了加载指示器,基本上在进程运行时弹出模态动画微调器或其他东西,然后在完成隐藏微调器时弹出。

    但是,如果您要进入越来越长的流程,我认为您真的应该考虑某种可以从 UI 中卸载它的后台处理。它不必是基于消息的工作人员,但即使是最终用户上传文件并在数据库中设置“待处理”条目。然后,您可以在后台定期安排pyramid script 轮询状态表并运行它找到的任何内容。您可以将视图中的文件处理移动到单独的方法中,并且可以从命令行脚本中调用该方法。然后,当处理完成时,它可以更新状态表,指示它已完成,并且可以在某处将反馈呈现给用户,而不会一直阻塞他们的 UI。

    【讨论】:

    • 您的第二段很有趣,但我认为没有办法在用户当前所在的页面(或每个页面)上不使用某种形式的自动重新加载选项的情况下向用户展示?需要客户端轮询,在这种情况下我基本上是在做 AJAX?
    • 是的,我可能会有某种 AJAX-y 背景 javascript 提供等待指示器或某种进度指示器,,我想有点像YouTube 是如何做到的。在您上传文件的地方,您会看到一个上传进度表,然后它就“完成”了。他们正在对文件进行后台处理,并且有一个状态页面,您可以在其中看到一些指示,但您不必在初始上传时等待他们的处理完成。
    猜你喜欢
    • 2017-03-26
    • 1970-01-01
    • 2021-07-22
    • 2012-04-05
    • 1970-01-01
    • 1970-01-01
    • 2011-05-23
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多