介绍
APScheduler(Advanced Python Scheduler)是一个非常强大的调度库,它允许我们在特定的时间间隔、特定的时间点或特定的日期执行任务。它支持多种调度器,例如基于日期、时间间隔和Cron表达式的调度。
安装
首先,我们需要安装APScheduler。可以使用以下命令通过pip进行安装:
pip install apscheduler
基本用法
APScheduler 的主要组件包括:
- 触发器(Triggers):定义了任务何时触发。
- 作业存储(Job Stores):用于存放任务,默认存储在内存中,也可以存储在数据库中。
- 执行器(Executors):用于执行任务。
- 调度器(Scheduler):是整个框架的核心,负责将触发器、作业存储和执行器组合在一起。
调度器
APScheduler主要有四种调度器:
BlockingScheduler
:阻塞式调度器BackgroundScheduler
:后台调度器AsyncIOScheduler
:基于 asyncio 的调度器TornadoScheduler
:为 Tornado 框架设计的调度器
选择哪种调度器取决于你的应用程序类型和需求。BlockingScheduler
是最简单的调度器,它会阻塞当前线程直到所有任务完成,适用于简单的任务调度。BackgroundScheduler
这是一个后台调度器,适用于在应用程序中运行,而不会阻塞主线程。它在一个单独的线程中运行调度器,允许你的程序继续执行其他任务。适用于 web 应用程序、服务端应用程序或其他需要长时间运行的应用程序。这两种调度器能够满足日常使用的大多数需求,本文主要讲解BlockingScheduler
和BackgroundSchedule
调度器的使用。不涉及执行器和作业存储等进阶用法。
触发器
APScheduler 调度器支持多种触发器,这些触发器可以用来定义任务应该何时执行。以下是一些可用的触发器类型:
1. 固定间隔(Interval)触发器
固定间隔(Interval)触发器是 APScheduler 中最简单的一种触发器,它允许你以固定的时间间隔执行任务。这种触发器适用于那些需要以恒定频率重复执行的任务。例如,每分钟检查系统状态或服务器的运行情况。每隔一段时间刷新缓存数据。每天定时备份文件或数据库。
固定间隔触发器接受以下参数:
weeks
(int):表示间隔的周数。days
(int):表示间隔的天数。hours
(int):表示间隔的小时数。minutes
(int):表示间隔的分钟数。seconds
(int):表示间隔的秒数。start_date
(datetime):任务首次运行的时间。end_date
(datetime):任务结束执行的时间。timezone
(datetime.tzinfo):用于计算日期和时间的时区。
以下是一些使用固定间隔触发器的示例:
-
每隔30分钟执行一次任务:
scheduler.add_job(my_job, 'interval', minutes=30)
-
每隔2秒执行一次任务,并指定时区:
from apscheduler.schedulers.background import BlockingScheduler from datetime import datetime import pytz timezone = pytz.timezone('Asia/Shanghai') scheduler = BlockingScheduler(timezone=timezone) scheduler.add_job(my_job, 'interval', seconds=2) scheduler.start()
固定间隔触发器的简单性和灵活性使其成为许多重复性任务的理想选择。不过,需要注意的是,如果任务的执行时间超过了间隔时间,APScheduler 会尝试尽快追赶进度,这可能会导致任务并发执行。为了避免这种情况,可以使用 coalesce
参数设置为 True
,这样如果任务错过了几次执行,只会执行一次。
2. 定时(Cron)触发器
APScheduler 的 cron
触发器是基于 cron-like 的时间表达式,它允许你以非常灵活的方式安排任务。Cron 触发器非常适合那些需要以特定的时间间隔(如每小时、每天、每周、每月等)执行的任务。这种触发器基于 cron 的时间表达式,允许你以非常灵活的方式安排任务。例如,每天凌晨1点执行任务可以使用 cron
触发器,并设置 hour=1
和 minute=0
。Cron 触发器适用于以下场景:
- 定时报告生成:例如,每月的第一天生成上个月的报告。
- 数据备份:例如,每天凌晨1点备份数据库。
- 定时清理任务:例如,每周清理日志文件。
- 定时提醒:例如,每天的早上9点发送工作提醒。
- 定时更新:例如,每小时的第30分钟检查是否有软件更新。
Cron 触发器接受以下参数:
year
(int|str):年(4位数字)month
(int|str):月(1-12)day
(int|str):日(1-31)week
(int|str):周(0-53,其中 0 和 53 代表每年的第一周和最后一周)day_of_week
(int|str):星期几(0-6,其中 0 是星期天)hour
(int|str):小时(0-23)minute
(int|str):分钟(0-59)second
(int|str):秒(0-59)
这些参数可以是整数或者字符串表达式。字符串表达式可以是以下形式:*
表示所有有效的值。*/a
表示每 a 步长。a-b
表示范围从 a 到 b。a,b,c
表示列举的值。
以下是一些使用 Cron 触发器的示例:
-
每天早上6点30分执行:
scheduler.add_job(my_job, 'cron', day='*', hour=6, minute=30)
-
每周一、三、五的下午5点执行:
scheduler.add_job(my_job, 'cron', day='mon,wed,fri', hour=17, minute=0)
-
每月的最后一天中午12点执行:
scheduler.add_job(my_job, 'cron', day='last', hour=12, minute=0)
-
每5分钟执行一次:
scheduler.add_job(my_job, 'cron', minute='*/5')
Cron 触发器提供了极大的灵活性,几乎可以满足任何定时任务的需求。不过,由于它的表达式相对复杂,使用时需要确保表达式正确无误。
3. 日期(Date)触发器
APScheduler 的 date
触发器是一种非常简单的触发器,它用于在特定的时间点执行一次任务。这种触发器不适用于需要重复执行的任务,而是用于一次性任务或在特定时间点触发的任务。date
触发器适用于以下场景:
- 一次性任务:例如,在将来的某个时间点发送通知或执行特定的操作。
- 计划事件:例如,在特定的日期和时间启动促销活动或执行系统维护。
- 延迟执行:例如,在程序启动后的一段时间内执行某个任务。
date
触发器接受以下参数:
run_date
(datetime):这是一个必填参数,用于指定任务应该运行的确切时间。这个时间应该是未来时间,否则任务将不会被触发。timezone
(datetime.tzinfo):这是一个可选参数,用于指定run_date
的时间 zone。如果未指定,默认使用本地时区。
以下是一些使用 date
触发器的示例:
- 在指定的时间点执行一次任务:
from datetime import datetime
from apscheduler.schedulers.background import BackgroundScheduler
def my_job():
print("执行任务")
# 指定运行时间
run_date = datetime(2023, 4, 1, 9, 30)
# 创建调度器
scheduler = BackgroundScheduler()
# 添加任务
scheduler.add_job(my_job, 'date', run_date=run_date)
# 启动调度器
scheduler.start()
# 保持程序运行
try:
while True:
time.sleep(2)
except (KeyboardInterrupt, SystemExit):
scheduler.shutdown()
- 在程序启动后5分钟执行一次任务:
from datetime import datetime, timedelta
from apscheduler.schedulers.background import BackgroundScheduler
def my_job():
print("执行任务")
# 创建调度器
scheduler = BackgroundScheduler()
# 添加任务,设置5分钟后的时间为运行时间
scheduler.add_job(my_job, 'date', run_date=datetime.now() + timedelta(minutes=5))
# 启动调度器
scheduler.start()
# 保持程序运行
try:
while True:
time.sleep(2)
except (KeyboardInterrupt, SystemExit):
scheduler.shutdown()
调度模板
通用的调度模板如下:
from apscheduler.schedulers.blocking import BlockingScheduler
from apscheduler.events import EVENT_JOB_EXECUTED, EVENT_JOB_ERROR
from datetime import datetime
# 定义一个任务函数,该函数将在调度器调度时被调用
def my_task():
print(f"Task executed at {datetime.now()}")
# 创建一个BlockingScheduler实例
scheduler = BlockingScheduler()
# 添加任务到调度器
# interval调度,每隔10秒钟运行一次任务
scheduler.add_job(
my_task, # 任务函数
'interval', # 调度方式:间隔调度
seconds=10, # 时间间隔:每10秒执行一次
id='my_task_id', # 任务ID:方便后续管理
replace_existing=True # 如果存在相同ID的任务,则替换
)
# cron调度示例,每天的8:30执行一次任务
# scheduler.add_job(
# my_task, # 任务函数
# 'cron', # 调度方式:Cron表达式
# hour=8, # 每天的小时部分
# minute=30, # 分钟部分
# id='cron_task_id', # 任务ID:方便后续管理
# replace_existing=True # 如果存在相同ID的任务,则替换
# )
# date调度示例,在2024年6月21日10:00执行一次任务
# run_date = datetime(2024, 6, 21, 10, 0, 0)
# scheduler.add_job(
# my_task, # 任务函数
# 'date', # 调度方式:指定日期
# run_date=run_date, # 运行日期和时间
# id='date_task_id', # 任务ID:方便后续管理
# replace_existing=True # 如果存在相同ID的任务,则替换
# )
# 定义错误处理函数, 该函数将在任务抛出异常时被调用
def error_listener(event):
if event.exception:
print(f'The job crashed : {event.exception}')
else:
print('The job executed successfully!')
# 添加错误处理监听器
scheduler.add_listener(
error_listener, # 错误处理函数
EVENT_JOB_EXECUTED | EVENT_JOB_ERROR # 监听任务执行和错误事件
)
# 启动调度器 BlockingScheduler的start方法会阻塞当前线程
try:
scheduler.start()
except (KeyboardInterrupt, SystemExit):
# 捕获中断和退出信号,安全关闭调度器
scheduler.shutdown()