Web2py - Run Loop&断开循环而不锁定应用程序

时间:2015-05-27 02:21:04

标签: python selenium web2py

不确定如何处理此类问题(更不用说有效地做到了)。

在我的应用程序中,用户应该能够单击按钮以通过ajax启动进程。他们也应该能够通过单击停止按钮随时停止该过程。类似的东西:

def start():
    session.test = True 
    items=["1","2","3","4"]
    while session.test == True:
        for item in items:
           ..do stuff..
        pass 

def stop():
    session.test = False

然而,这不起作用。在这些问题中,当启动循环时,应用程序会锁定。如何在不锁定应用程序的情况下让多个用户运行自己的循环(并且能够突破它们)?

编辑:

我应该注意到..do的东西..正在利用selenium浏览器。

2 个答案:

答案 0 :(得分:2)

当涉及长时间运行的任务时,在web2py模型,视图或控制器中执行该任务是一个坏主意,因为这将阻止整个请求线程,直到任务完成。与此同时,这也会强制服务器为后续对该任务的请求打开新线程。如果您有太多活动请求,您的整个网站将会变慢或锁定。 (周围有解决方案,比如用gevent修补web2py,但这是一个高级主题)

在web2py中,最好将长时间运行的任务放在其自己的进程(与应用程序进程分开)中,并让任务通过数据库与应用程序进行通信。 Web2py有多种解决方案,如Cron,自制任务队列和调度程序。 See here

让我们做自制任务队列,因为它是最简单的。在此示例中,我们希望发送电子邮件以确认图书订单。目前我们发送这样的电子邮件:

def controller():
     #... controller stuff (i.e. buyer purchased a book)
     #... send a confirmation email
     mail.send(
          to = 'buyer@example.com',
          subject = 'Bookstore order',
          message ='Thank you for your order! Your new book will ship within 24 hours.',
     )
     #... 5 seconds later (blocks)
     #... continue

但是,这会阻止发送电子邮件(例如5秒)所需的整个时间的请求,因为mail.send()是一个运行缓慢的任务。这对用户体验不利,因为用户将看到他的浏览器加载5秒钟。

让我们将电子邮件推送到数据库,并让一个单独的后台进程轮询数据库以查找新电子邮件,并将其发送出去。例如,在我们的控制器中:

def controller():
     #... controller stuff (i.e. buyer purchased a book)
     #... push confirmation email to db
     db.queue.insert(status='pending',
          email='buyer@example.com',
          subject = 'Bookstore order',
          message ='Thank you for your order! Your new book will ship within 24 hours.',
     )
     #... milliseconds later (no block)
     #... continue

注意这次完成的时间是多少,并且用户不会看到他的浏览器减速。

在我们的web2py / private文件夹中,让我们在mail_queue.py中编写我们的小后台脚本:

## in file /bookstore/private/mail_queue.py
import time
while True:
    rows = db(db.queue.status=='pending').select()
    for row in rows:
        if mail.send(to=row.email,
            subject=row.subject,
            message=row.message):
            row.update_record(status='sent')
        else:
            row.update_record(status='failed')
        db.commit()
    time.sleep(60) # poll every minute for new emails

注意它的行为就像一个控制器,因为你可以访问与控制器相同的全局变量。唯一的区别是你必须使用db.commit()来保存对db的更改。

最后在shell中,让我们启动后台进程。请注意,它将与web2py应用程序处于单独的进程中,并且不会相互干扰。

python web2py.py -S app -M -R applications/bookstore/private/mail_queue.py

答案 1 :(得分:0)

任何基于事件循环的程序,无论是服务器还是GUI,都不能在事件处理程序中执行像while这样的长期运行。如果你试试,你会得到你所看到的。当程序运行while循环时,主事件循环未运行。因此,start必须立即返回。

这有两种通用解决方案:

  • 多线程。 start函数启动后台线程(或进程)来完成工作,然后返回。当然,这意味着必须在循环和程序的其余部分(例如session.test)之间共享的任何内容都需要受到锁或其他同步对象的保护。
  • 分解循环,以便它只安排一个回调以便稍后运行。该回调只检查您是否已完成,如果没有,则运行循环的一个步骤,并安排自己再次运行。当然这意味着将你的逻辑从内到外。

这两者都有变化。您可以使用子进程或greenlet样式的用户线程,而不是线程。或者你可以在promises或coroutines中包装回调。但这些是两个基本思想。你必须选择一个,并对其进行阅读。