如何从尽快运行的CherryPy BackgroundTask返回数据

时间:2016-04-15 10:42:32

标签: python python-2.7 cherrypy python-multithreading

我正在使用CherryPy构建一个Web服务,用于迭代批处理数据。理想的工作流程如下:

  1. 用户将数据发布到服务以进行处理
  2. 当处理作业空闲时,它会收集排队的数据并开始另一次迭代
  3. 当作业正在处理时,用户正在将更多数据发布到队列以进行下一次迭代
  4. 当前迭代完成后,结果将被传回,以便用户可以使用相同的API获取它们。
  5. 下一批排队数据再次启动作业。
  6. 这里的关键考虑因素是,无论队列中的数据量如何,处理都应尽可能快地运行,每次迭代都会在上一次迭代完成后立即开始。每次迭代可以花多长时间没有上限,因此无法为其运行创建固定的时间表。

    有一些使用BackgroundTasklike this one)的示例,但我还没有找到一个处理返回数据的示例,或者一个处理尽可能快地运行的任务的示例,而不是在固定的时间表上。

    我并没有坚持BackgroundTask解决方案,所以如果有人可以提供替代方案,我会非常高兴。感觉就像在框架内有一个解决方案。

1 个答案:

答案 0 :(得分:2)

不要使用BackgroundTask解决方案运行后台任务,因为它会在一个帖子中运行,并且由于GIL,我们无法回答新问题要求。使用在不同流程中运行后台任务的队列解决方案,例如CeleryRQ

我将使用RQ详细开发一个示例。 RQ使用Redis作为消息代理,因此首先需要安装并启动Redis。

然后使用长时间运行的后台方法创建一个模块(在我的示例中为mytask):

import time
def long_running_task(value):
    time.sleep(15)
    return len(value)

启动一个(如果你想并行运行任务,则启动一个)RQ工作者,运行你的工作人员的python可以访问你的mytask模块是非常重要的(导出PYTHONPATH)如果你的模块不在路径中,那么在运行worker之前:

# rq worker

上面你有一个非常简单的cherrypy webapp,它展示了如何使用RQ队列:

import cherrypy
from redis import Redis
from rq import Queue    
from mytask import long_running_task


class BackgroundTasksWeb(object):

    def __init__(self):
        self.queue = Queue(connection=Redis())
        self.jobs = []

    @cherrypy.expose
    def index(self):
        html =  ['<html>', '<body>']
        html += ['<form action="job">', '<input name="q" type="text" />', '<input type="submit" />', "</form>"]
        html += ['<iframe width="100%" src="/results" />']
        html += ['</body>', '</html>']
        return '\n'.join(html)

    @cherrypy.expose
    def results(self):
        html = ['<html>', '<head>', '<meta http-equiv="refresh" content="2" >', '</head>', '<body>']
        html += ['<ul>']
        html += ['<li>job:{} status:{} result:{} input:{}</li>'.format(j.get_id(), j.get_status(), j.result, j.args[0]) for j in self.jobs]
        html += ['</ul>']
        html += ['</body>', '</html>']
        return '\n'.join(html)

    @cherrypy.expose
    def job(self, q):
        job = self.queue.enqueue(long_running_task, q)
        self.jobs.append(job)
        raise cherrypy.HTTPRedirect("/")


cherrypy.quickstart(BackgroundTasksWeb())

在生产webapp中,我会使用jinja2模板引擎来生成html,并且很可能是websockets来更新Web浏览器中的作业状态。