使用APScheduler开启定时任务

前言

相比基于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异步队列的使用。

你可能感兴趣的:(使用APScheduler开启定时任务)