前言
相比基于Linux的crontab定时任务模块来说,在Python中使用APScheduler创建定时任务更加方便、精确,编写方式也更符合Python风格,毕竟crontab里的各种**看得我心慌。且APScheduler也更适用于Django、Flask环境中添加动态任务。这篇文章就APScheduler 的使用做个简单总结。
从一个简单示例开始
先写个简单定时器示例,从代码中理解APScheduler的使用方法:
from apscheduler.schedulers.background import BackgroundScheduler
from apscheduler.executors.pool import ProcessPoolExecutor
# 执行器配置:使用线程池运行定时任务,且最大线程数为5个
executors = {
'default': ThreadPoolExecutor(max_workers=5)
}
# 配置一个调度器对象
scheduler = BackgroundScheduler(executors=executors)
# 给调度器添加定时任务task_func, 指定每日的3点运行一次
scheduler .add_job(task_func, "cron", hour=3)
# 开启定时任务
scheduler .start()
以上便是创建一个每日定时运行一次的定时任务的基本方式。
配置说明
使用apscheduler配置并开启定时任务的方法大致了解后,再去了解更多的配置项和对应实现的功能。
- 安装方式:
pip install apscheduler
1. 调度器Scheduler
要实现定时任务,首先需要初始化一个调度器对象,例如上例中使用的调度器为BackgroundScheduler
类,只需scheduler = BackgroundScheduler()
便创建出一个调度器对象。
- BackgroundScheduler类
导入方式:from apscheduler.schedulers.background import BackgroundScheduler
适用于在框架环境中使用,如Django、Flask中,该调度器实例在调用.start()
方法后不会发生阻塞,而是如其名一样在后台运行,不影响框架中其他代码的执行。
- BlockingScheduler类
from apscheduler.schedulers.blocking import BlockingScheduler
适用于作为独立进程时使用,在自己定义的脚本程序中使用BlockingScheduler类更加方便,该调度器实例在调用.start()
方法后会阻塞,等待下一个定时时间点的到来再继续执行。
2. 执行器executors
在示例中使用的executor是线程池ThreadPoolExecutor
的方式,对应着,当然也可以使用进程池ProcessPoolExecutor
的方式。
其内部调用的本身是Python内置的concurrent.futures
模块下的ThreadPoolExecutor及ProcessPoolExecutor类。
- ThreadPoolExecutor线程池
以多线程的方式运行定时任务,一般情况使用该方案即可。 - ProcessPoolExecutor进程池
以多进程的方式运行定时任务,当任务为CPU密集型时,应考虑选择该方案,当然,该方案会更加消耗主机资源。
使用方法如示例所示,以字典方式定义执行器,传入最大线程或进程数后,在调度器实例中指定,或使用.add_executor()
添加即可。
executors = {
'default': ThreadPoolExecutor(20)
}
scheduler = BackgroundScheduler(executors=executors)
# 也可使用下面的方式添加执行器
# scheduler.add_executor()
3. 触发器Trigger
在使用sched.add_job()
方法给调度器添加任务时,需要传入定时启动的方式和规定的运行时间。
以下是add_job
方法源码中定义可传入的参数:
def add_job(self, func, trigger=None, args=None, kwargs=None, id=None, name=None,
misfire_grace_time=undefined, coalesce=undefined, max_instances=undefined,
next_run_time=undefined, jobstore='default', executor='default',
replace_existing=False, **trigger_args):
第一个参数func为需要定时执行的任务函数名;
第三、四个参数为定时任务func的参数,如原func函数有参数,以列表或字典的形式传入即可。
第二个trigger即为触发器,指定执行任务的时机,常见的trigger参数有以下3个,具体用法释义在每个代码注释中包含:
- 1)date 在特定的时间日期执行
from datetime import date
# 在2019年11月6日00:00:00执行
scheduler.add_job(my_job1, 'date', run_date=date(2009, 11, 6))
# 在2019年11月6日16:30:05执行,两种写法均可
scheduler.add_job(my_job2, 'date', run_date=datetime(2009, 11, 6, 16, 30, 5))
scheduler.add_job(my_job3, 'date', run_date='2009-11-06 16:30:05')
# 立即执行,不指定具体时间时会立即执行任务
scheduler.add_job(my_job4, 'date')
scheduler.start()
- 2)interval 经过指定的时间间隔执行
from datetime import datetime
# 每两小时执行一次
scheduler.add_job(job_function, 'interval', hours=2)
# 在2019年10月10日09:30:00 到2020年6月15日的时间内,每两小时执行一次
scheduler.add_job(job_function, 'interval', hours=2, start_date='2019-10-10 09:30:00', end_date='2020-06-15 11:00:00')
interval可使用的时间参数如下:
weeks (int) – 等待的周数
days (int) – 等待的天数
hours (int) – 等待的小时数
minutes (int) – 等待的分钟数
seconds (int) – 等待的秒数
start_date (datetime|str) – 区间计算的起点时间
end_date (datetime|str) – 区间计算的终止时间
timezone (datetime.tzinfo|str) – 用于日期/时间计算的时区
- 3)cron按指定的周期执行
在最初的示例中使用的便是该类触发器,按指定的周期循环运行。
# 在每天的3:00点运行
scheduler.add_job(task_func, "cron", hour=3)
# 在每个6、7、8、11、12月的第三个周五的00:00, 01:00, 02:00和03:00 执行
scheduler.add_job(job_function, 'cron', month='6-8,11-12', day='3rd fri', hour='0-3')
# 在2019年5月30日前的周一到周五的5:30执行
scheduler.add_job(job_function, 'cron', day_of_week='mon-fri', hour=5, minute=30, end_date='2019-05-30')
cron可使用的时间参数如下:
year (int|str) – 年(4位数的年份)
month (int|str) – 月(1-12)
day (int|str) – 日 (1-31)
week (int|str) – ISO周日历 (1-53)
day_of_week (int|str) – 英文简写或数字表示的星期几 (0-6 或者 mon,tue,wed,thu,fri,sat,sun)
hour (int|str) – 时 (0-23)
minute (int|str) – 分 (0-59)
second (int|str) – 秒 (0-59)
start_date (datetime|str) – 最早可能触发的日期/时间(包括在内)
end_date (datetime|str) – 最晚可能触发的日期/时间(包括在内)
timezone (datetime.tzinfo|str) – 用于日期/时间计算的时区 (默认为调度程序的时区)
触发器的参数看起来内容太多,但其实都不用记住,只需在明确需求后去上述参数中查找,组合并设置自己想指定的定时方案即可。
4. 任务储存器
除以上3个核心功能外,定时任务还可以指定持久储存方案。
默认的储存器使用的是使用内存,当任务意外中断,重启后定义的任务也会重新被添加到调度器,简单而高效;
但当作业中断需要从中断中恢复任务,则在配置时应该使用数据库来作为任务储存器,具体使用什么数据库,可以自行选择SQLite、MongoDB、Redis。
定义方法如下,这里不做过多介绍,如需使用,再查找相关文档:
from apscheduler.jobstores.sqlalchemy import SQLAlchemyJobStore
jobstores = {
'mongo': {'type': 'mongodb'},
'default': SQLAlchemyJobStore(url='sqlite:///jobs.sqlite')
}
scheduler = BackgroundScheduler(executors=executors, jobstores=jobstores)
以上。
最后,如果需要创建的定时任务太多,应该了解学习下RabbitMQ异步队列的使用。