如何在Python Flask框架中运行重复任务?

时间:2014-09-03 08:01:49

标签: python cron flask apscheduler

我正在建立一个向访问者提供一些信息的网站。此信息通过每5秒轮询一对外部API在后台聚合。我现在的工作方式是使用APScheduler个工作。我最初更喜欢APScheduler,因为它使整个系统更容易移植(因为我不需要在新机器上设置cron作业)。我按如下方式启动轮询功能:

from apscheduler.scheduler import Scheduler

@app.before_first_request
def initialize():
    apsched = Scheduler()
    apsched.start()

    apsched.add_interval_job(checkFirstAPI, seconds=5)
    apsched.add_interval_job(checkSecondAPI, seconds=5)
    apsched.add_interval_job(checkThirdAPI, seconds=5)

这种方法有用,但它有些麻烦:

  1. 对于初学者来说,这意味着interval-jobs在Flask上下文之外运行。到目前为止,这并不是一个很大的问题,但是当调用端点失败时,我希望系统向我发送一封电子邮件(说"他们称API X失败")。但是,由于它并未在Flask环境中运行,因此它抱怨flask-mail无法执行(RuntimeError('working outside of application context'))。
  2. 其次,我想知道当我不再使用Flask内置调试服务器时这将会如何表现,但是生产服务器可以说4个工作人员。那会是每次工作四次吗?
  3. 总而言之,我觉得应该有更好的方法来运行这些重复的任务,但我不确定如何。有没有人有这个问题的有趣解决方案?欢迎所有提示!

    [编辑] 我刚刚用Celery阅读schedules。虽然我不太了解Celery与APScheduler的区别,以及它是否可以解决我的两点,但我想知道是否有人认为我应该在Celery中进行更多调查?

    【结论】 大约两年后,我读了这篇文章,我想我可以让你们知道我最终得到了什么。我认为@BluePeppers说得对,我不应该与Flask生态系统紧密联系。所以我选择使用Ansible设置的每分钟运行的常规cron-jobs。虽然这使它变得有点复杂(我需要学习Ansible并转换一些代码以便每分钟运行它就足够了)我认为这更加强大。 我目前正在使用令人敬畏的pythonr-rq来排队同步作业(检查API和发送电子邮件)。我刚刚发现了rq-scheduler。我还没有对它进行过测试,但它似乎正是我所需要的。所以这可能是未来读者对这个问题的一个提示。

    其余的,我希望你们大家度过美好的一天!

1 个答案:

答案 0 :(得分:24)

(1)

您可以使用app.app_context()上下文管理器来设置应用程序上下文。我想用法会是这样的:

from apscheduler.scheduler import Scheduler

def checkSecondApi():
    with app.app_context():
        # Do whatever you were doing to check the second API

@app.before_first_request
def initialize():
    apsched = Scheduler()
    apsched.start()

    apsched.add_interval_job(checkFirstAPI, seconds=5)
    apsched.add_interval_job(checkSecondAPI, seconds=5)
    apsched.add_interval_job(checkThirdAPI, seconds=5)

或者,您可以使用装饰器

def with_application_context(app):
    def inner(func):
        @functools.wraps(func)
        def wrapper(*args, **kwargs):
            with app.app_context():
                return func(*args, **kwargs)
        return wrapper
    return inner

@with_application_context(app)
def checkFirstAPI():
    # Check the first API as before

(2)

是的,它仍然可以使用。唯一(重大)差异是您的申请不会直接与世界沟通;它将通过反向代理或通过fastcgi / uwsgi /等等。唯一的问题是,如果您有多个应用程序启动实例,那么将创建多个调度程序。为了解决这个问题,我建议你将后端任务从Flask应用程序中移出,并使用专为定期运行任务而设计的工具(即Celery)。这样做的缺点是你将无法使用像Flask-Mail这样的东西,但是imo,与Flask生态系统紧密联系并不是太好了;你在Flask-Mail上使用标准的非Flask邮件库获得了什么?

此外,与拥有一个单一的Web应用程序相比,分解应用程序可以更容易地扩展单个组件,因为需要容量。