配置调度器
APScheduler提供多种方式来配置调度器。可以使用字典进行配置,也可以通过声明一些关键变量。也可以先实例化一个调度器,然后去配置它。
不同调度器的配置共同选项可以再BaseSceduler里查看。该类的子类额外的配置选项可以到子类相关的API说明里找到。作业仓库和处理器也是如此。
假设你想使用BackgroundScheduler调度器,并且想使用默认的作业仓库和默认的处理器在你的应用程序里,代码可如下:
from apscheduler.schedulers.background import BackgroundScheduler scheduler = BackgroundScheduler() # Initialize the rest of the application here, or before the scheduler initialization
此时你创建的调度器将采用内存存储,并且使用线程池处理器且最大线程为10。现在,假设你需要额外的配置。如你想使用两个作业仓库,两个处理器,并且想为你的调度器设置不同的时区和为作业仓库设置不同的默认值。我们将设你需要的是这些:
有如下策略:
方法1:
from pytz import utc from apscheduler.schedulers.background import BackgroundScheduler from apscheduler.jobstores.mongodb import MongoDBJobStore from apscheduler.jobstores.sqlalchemy import SQLAlchemyJobStore from apscheduler.executors.pool import ThreadPoolExecutor, ProcessPoolExecutor jobstores = { 'mongo': MongoDBJobStore(), 'default': SQLAlchemyJobStore(url='sqlite:///jobs.sqlite') } executors = { 'default': ThreadPoolExecutor(20), 'processpool': ProcessPoolExecutor(5) } job_defaults = { 'coalesce': False, 'max_instances': 3 } scheduler = BackgroundScheduler(jobstores=jobstores, executors=executors, job_defaults=job_defaults, timezone=utc)
方法2:
from apscheduler.schedulers.background import BackgroundScheduler # The "apscheduler." prefix is hard coded scheduler = BackgroundScheduler({ 'apscheduler.jobstores.mongo': { 'type': 'mongodb' }, 'apscheduler.jobstores.default': { 'type': 'sqlalchemy', 'url': 'sqlite:///jobs.sqlite' }, 'apscheduler.executors.default': { 'class': 'apscheduler.executors.pool:ThreadPoolExecutor', 'max_workers': '20' }, 'apscheduler.executors.processpool': { 'type': 'processpool', 'max_workers': '5' }, 'apscheduler.job_defaults.coalesce': 'false', 'apscheduler.job_defaults.max_instances': '3', 'apscheduler.timezone': 'UTC', })
方法3:
from pytz import utc from apscheduler.schedulers.background import BackgroundScheduler from apscheduler.jobstores.sqlalchemy import SQLAlchemyJobStore from apscheduler.executors.pool import ProcessPoolExecutor jobstores = { 'mongo': {'type': 'mongodb'}, 'default': SQLAlchemyJobStore(url='sqlite:///jobs.sqlite') } executors = { 'default': {'type': 'threadpool', 'max_workers': 20}, 'processpool': ProcessPoolExecutor(max_workers=5) } job_defaults = { 'coalesce': False, 'max_instances': 3 } scheduler = BackgroundScheduler() # .. do something else here, maybe add jobs etc. scheduler.configure(jobstores=jobstores, executors=executors, job_defaults=job_defaults, timezone=utc)
启动调度器
调用start()方法即可。对于BlockingScheduler,必须初始化之前才能开启,因为一旦开始,就不允许再进行设置。其他类型的调度器,开始之后,仍然可以进行相关设置。
添加作业
方法1.调用add_job()
方法2.使用装饰器函数scheduled_job()
方法1最为常用, add_job()函数会返回一个Job实例,方便你去修改或者删除作业。而如果作业在应用程序运行时不会更改,那是用方法2将会十分方便。
在调度器运行时,你可以随时调度作业。但如果当添加作业时,调度器没有运行。将会临时调度一下作业,但此次调度时间不会记为作业的首次运行时间。只有当调度器运行时,才会被记作首次运行时间。
有必要提醒的是,如果你使用一个处理器或者作业仓库串行执行作业,需要满足下列需求:
关于內建的作业仓库,只有内存仓库不需要串行作业,关于內建的处理器,只有进程池处理器需要串行作业。
如果你打算在你应用程序初始化时,使用持久作业仓库调度作业,你必须为每个作业设定一个精确的ID。使用replace_existing=True,否则你需要每次在你的应用程序重启时,必须将原有的作业拷贝。如果想要添加作业后立即执行,则不必设定触发器。
移除作业
从调度器移除作业之后,作业将会从作业仓库移除,并不再被执行。有两种方法:
后一种可能更为方便,但是他需要你添加作业时在某处存储作业实例。针对使用装饰器scheduled_job(),只能使用第一种方法。
举例-方法1
cheduler.add_job(myfunc, 'interval', minutes=2, id='my_job_id') scheduler.remove_job('my_job_id')
举例-方法2
job = scheduler.add_job(myfunc, 'interval', minutes=2) job.remove()
如果一个作业不再被调度(比如日期触发器触发之后,不再触发)。作业将会自动被移除。
中止和恢复作业
通过作业实例或者调度器可以很容易地停止或者恢复作业,当作业中止之后,下次执行时间将会被清除,并且不会再有新的执行时间直到作业被恢复。
中止方法如下:
apscheduler.job.Job.pause()
apscheduler.schedulers.base.BaseScheduler.pause_job()
恢复方法如下:
apscheduler.job.Job.resume()
apscheduler.schedulers.base.BaseScheduler.resume_job()
获取调度作业列表
调用get_jobs()将会返回作业实例列表,如果你想获取某个作业仓库下的作业,讲参数作业仓库别名传入。
使用print_jobs()可以打印出下面信息表:作业、对应的触发器、下次执行时间
修改作业
调用apscheduler.job.Job.modify()或者modify_job(),除了ID无法修改,其余均可更改。
例如
job.modify(max_instances=6, name='Alternate name')
如果你想更改触发器,可以使用apscheduler.job.Job.reschedule()或者reschedule_job()。这个方法会为这个作业生成一个新的触发器,并且会重新计算下次运行的时间。
scheduler.reschedule_job('my_job_id', trigger='cron', minute='*/5')
关闭调度器
调用scheduler.shutdown()
该方法默认会先关闭作业仓库和处理器,然后等待当前进行的作业结束后,再关闭。如果想直接关闭,传入参数wait=Flase。
中止和恢复正在进行的作业
scheduler.pause()中止作业,进入休眠状态
scheduler.resume()恢复作业,进入唤醒状态
scheduler.start(paused=True)启动作业,休眠状态的将不会唤醒
限制执行作业的实例数
默认情况,在同一时间,一个作业只允许一个实例。这意味着如果上一触发时间作业还没有执行完,下一个触发时间将不会被执行。(下面的英文说下一次的触发称作misfire,意思是失火,不好理解。举个现实的例子,假如我有个任务是每5秒更新一下缓存数据,通过REST ful接口获取,如果又一次发生超时,那么就没法获取最新的数据来缓存。这是很严重的事故。允许开启多个实例也就是这个意思。misfire应该是特有的术语,可以理解为要执行的任务没被执行,就是misfire)指定max_instances就好。
未执行作业
有时会发生调度要执行的作业没有被执行,这种情况常发生在使用持久的作业仓库时,要执行的作业因为调度器关闭或者重启,而使得作业未被执行。这种作业就被成为misfired。调度器会通过作业的misfire_grace_time来监测没有执行的时间,来看这个作业能被再被触发。这会使得该作业被成功的执行多次。如果你不想被执行多次,你可以将未被执行的次数合并成一次。设置开启coalesing就可以了。
如果因为进程池或者线程池满了,而导致作业执行被延后,那处理器会跳过执行延后的作业。你可以通过增加进程数或者线程数来解决这个问题。也可以通过设misfire_grace_time来解决这个问题。
(理解一下,指定misfire_grace_time,会把没有执行的作业放到队列里,然后依次执行。如果不想任务在执行多次,开启coalesing就可以。)
调度器事件
可以为调度器的事件设置监听器,在某些情况下,可能会产生调度器事件,在某些特定的事件,可能会伴随着相关的细节信息。因此有必要坚挺调度器产生的事件。举例如下
def my_listener(event): if event.exception: print('The job crashed :(') else: print('The job worked :)') scheduler.add_listener(my_listener, EVENT_JOB_EXECUTED | EVENT_JOB_ERROR)
问题定位
可以代码示例通过如下来定位问题:
import logging logging.basicConfig() logging.getLogger('apscheduler').setLevel(logging.DEBUG)