确保只有一个工人启动apscheduler事件在金字塔的web应用程序运行多个工人(Make sur

2019-08-31 08:29发布

我们有一个Web应用程序与金字塔制成,通过gunicorn + nginx的服务。 它的工作原理与8工作者线程/进程

我们需要工作,我们选择apscheduler。 这里是我们如何启动它

from apscheduler.events import EVENT_JOB_EXECUTED, EVENT_JOB_ERROR
from apscheduler.scheduler import Scheduler

rerun_monitor = Scheduler()
rerun_monitor.start()
rerun_monitor.add_interval_job(job_to_be_run,\
            seconds=JOB_INTERVAL)

问题是,gunicorn的所有工作进程挑调度起来。 我们试图实现一个文件锁,但它似乎并不像一个足够好的解决方案。 什么是确保在任何给定的时间只有工作进程的挑选安排的事件并没有其他线程将它拾起到明年的最佳途径之一JOB_INTERVAL

该解决方案需要我们决定以后要切换到的Apache2 + modwsgi情况下,即使mod_wsgi的工作。 它需要与作为女服务员单一工艺开发服务器一起工作。

从赏金赞助商更新

我现在面临由OP描述了同样的问题,只是一个Django应用程序。 我主要是确定加入这个细节不会有太大变化,如果原来的问题。 出于这个原因,并获得更多的知名度,我还标有这个问题, django

Answer 1:

由于Gunicorn开始用8名工人(在你的例子),该应用8次进8点的过程。 这些8个过程从过程中,其监视他们的每个状态具有与添加/删除工人的能力分叉。

每个进程得到您的APScheduler对象,最初是您的主流程APScheduler的精确副本的副本。 这导致每个“第n”工人(过程)执行各作业的总的“n”次。

对此的一种黑客是用下列选项运行gunicorn:

env/bin/gunicorn module_containing_app:app -b 0.0.0.0:8080 --workers 3 --preload

--preload标志告诉Gunicorn为“ 建立该工作进程之前装入应用程序 ”。 通过这样做,每个工人“给应用程序的副本,已经由主实例,而不是实例化应用程序本身。” 这意味着下面的代码只能在主线程执行一次:

rerun_monitor = Scheduler()
rerun_monitor.start()
rerun_monitor.add_interval_job(job_to_be_run,\
            seconds=JOB_INTERVAL)

此外,我们需要设置作业存储比其他任何东西:内存:。这个方法,虽然每个工人是自己独立的进程无法与其他7连通的,通过使用本地数据库(而不是存储),我们保证单 - 点 - 的 - 真理的作业存储CRUD操作。

from apscheduler.schedulers.background import BackgroundScheduler
from apscheduler.jobstores.sqlalchemy import SQLAlchemyJobStore

rerun_monitor = Scheduler(
    jobstores={'default': SQLAlchemyJobStore(url='sqlite:///jobs.sqlite')})
rerun_monitor.start()
rerun_monitor.add_interval_job(job_to_be_run,\
            seconds=JOB_INTERVAL)

最后,我们希望使用的,因为其执行的BackgroundScheduler start() 当我们调用start()在BackgroundScheduler,一个新的线程在后台,负责调度/执行作业纺起来。 这是因为显著记得在步骤(1),由于我们--preload标志,我们只执行start()函数一次,在主Gunicorn过程。 根据定义, 派生进程不继承其父的线程,每个工人都不会运行BackgroundScheduler线程。

from apscheduler.jobstores.sqlalchemy import SQLAlchemyJobStore

rerun_monitor = BackgroundScheduler(
    jobstores={'default': SQLAlchemyJobStore(url='sqlite:///jobs.sqlite')})
rerun_monitor.start()
rerun_monitor.add_interval_job(job_to_be_run,\
            seconds=JOB_INTERVAL)

作为这一切的结果是,每Gunicorn工人有已被骗入“已启动”状态的APScheduler,但实际上没有运行,因为它降低了线程它的父! 每个实例也能够更新作业存储数据库,只是没有执行任何工作!

看看烧瓶APScheduler一个快速的方式在一个Web服务器(如Gunicorn)运行APScheduler,并启用CRUD操作为每个作业。



Answer 2:

我发现,有一个非常类似的问题一个Django项目工作的修复。 我只是第一次调度开始绑定TCP套接字对证其后。 我想下面的代码可以为您提供一些小改动正常工作。

import sys, socket

try:
    sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
    sock.bind(("127.0.0.1", 47200))
except socket.error:
    print "!!!scheduler already started, DO NOTHING"
else:
    from apscheduler.schedulers.background import BackgroundScheduler
    scheduler = BackgroundScheduler()
    scheduler.start()
    print "scheduler started"


文章来源: Make sure only one worker launches the apscheduler event in a pyramid web app running multiple workers