之前比较泛泛的介绍了 APScheduler
库,但是其中有一些模块的接口需要额外注意一下,本篇文章比较干,真正开发的时候,可以当成工具用
event
主要是 APScheduler
中触发的事件类,我们可以通过 add_listener()
为调度程序绑定监听函数,在收到指定事件后做一些自定义的操作
事件 | 对应枚举值 | 描述 | 归属类 |
---|---|---|---|
EVENT_SCHEDULER_STARTED | 1 | 调度程序启动 | SchedulerEvent |
EVENT_SCHEDULER_SHUTDOWN | 2 | 调度程序关闭 | SchedulerEvent |
EVENT_SCHEDULER_PAUSED | 4 | 调度程序中任务处理暂停 | SchedulerEvent |
EVENT_SCHEDULER_RESUMED | 8 | 调度程序中任务处理恢复 | SchedulerEvent |
EVENT_EXECUTOR_ADDED | 16 | 将执行器添加到调度程序中 | SchedulerEvent |
EVENT_EXECUTOR_REMOVED | 32 | 执行器从调度程序中删除 | SchedulerEvent |
EVENT_JOBSTORE_ADDED | 64 | 将任务存储添加到调度程序中 | SchedulerEvent |
EVENT_JOBSTORE_REMOVED | 128 | 任务存储从调度程序中删除 | SchedulerEvent |
EVENT_ALL_JOBS_REMOVED | 256 | 所有任务从所有任务存储中删除或从一个特定的任务存储中删除 | SchedulerEvent |
EVENT_JOB_ADDED | 512 | 任务添加到任务存储中 | JobEvent |
EVENT_JOB_REMOVED | 1024 | 从任务存储中删除了任务 | JobEvent |
EVENT_JOB_MODIFIED | 2048 | 从调度程序外部修改了任务 | JobEvent |
EVENT_JOB_EXECUTED | 4096 | 任务被成功执行 | JobExecutionEvent |
EVENT_JOB_ERROR | 8192 | 任务在执行期间引发异常 | JobExecutionEvent |
EVENT_JOB_MISSED | 16384 | 错过了任务执行 | JobExecutionEvent |
EVENT_JOB_SUBMITTED | 32768 | 任务已经提交到执行器中执行 | JobSubmissionEvent |
EVENT_JOB_MAX_INSTANCES | 65536 | 任务因为达到最大并发执行时,触发的事件 | JobSubmissionEvent |
EVENT_ALL | 包含以上的所有事件 |
不同事件因为基于不同的类,因此可以获取到的信息是不一致的
但 event
中有一个公共的字段 code
(枚举值), 所以可以通过 code
来识别出具体是哪一种事件,然后在获取具体的一些字段信息
主要分为4个类,主要看一下4个类下独有的字段:
alias
添加或删除的任务存储或执行程序的别名(如果有)job_id
任务的标识IDjobstore
包含相关任务的任务存储的别名job_id
任务的标识IDjobstore
包含相关任务的任务存储的别名scheduled_run_times
计划运行任务的时间列表job_id
任务的标识IDjobstore
包含相关任务的任务存储的别名scheduled_run_time
计划运行任务的时间retval
成功执行的任务的返回值exception
任务运行引起的异常traceback
任务出错的位置Job
包含了很多设置,它包含了 triggers(触发器)
, executor(执行器)
的信息,还有一些调度相关配置信息,例如允许运行的最多实例数,允许延迟执行的最长时间,是否合并等等,难点主要在创建,但是官方是不希望用户直接通过这个类来实例化 Job
, 而是通过调度程序 scheduler
中的 add_job()
来实现,这里简单了解一些 Job
保存一些信息,和关于操作 Job
的接口,方便我们之后阅读它的源码
实例化任务
scheduler
需要绑定的调度程序id(str)
任务的唯一标识符name(str)
任务的名称func
任务实际执行的函数args(tuple|list)
函数可能需要的传入参数kwargs(dist)
一些其他的可调用的关键字参数,比如描述触发器的相关字段等等coalesce(bool)
是否在多个运行时间到期时仅运行一次任务trigger
触发器对象executor(str)
执行器名称misfire_grace_time(int)
允许此任务延迟执行的最大时间(单位秒)max_instances(int)
该任务允许的并发执行的最大实例数量next_run_time(datetime.datetime)
此任务下一次计划运行的时间Return type
Job 的实例对该任务的配置进行修改,并将其保存在关联的任务存储中,可接受的关键字参数与创建此类的参数相同
Return type
Job 的实例暂停该任务的执行
取消任务,并将其从其关联的任务存储中删除
Return type
Job 的实例在此任务上切换触发器
Return type
Job 的实例如果之前已暂停,恢复该任务的计划
如果引用的任务仍在等待添加到其指定的任务存储中,则返回True。
触发器是值得比较详细介绍的,主要是触发方式有 4 种
date
指定时间运行的一次性任务inteval
间隔时间运行的循环任务cron
定时任务combining
组合任务date
是执行一次性任务,所有它的配置简单
run_date
任务执行的时间timezone
时间的时区from datetime import date
from apscheduler.schedulers.blocking import BlockingScheduler
sched = BlockingScheduler()
def my_job(text):
print(text)
#您可以指定应运行任务的确切时间:
sched.add_job(my_job, 'date', run_date=datetime(2020, 12, 16, 16, 30, 5), args=['text'])
# 运行日期也可以作为文本给出
sched.add_job(my_job, 'date', run_date='2020-12-16 16:30:05', args=['text'])
# 要添加要立即运行的任务
sched.add_job(my_job, args=['text'])
sched.start()
inteval
是配置间隔时间运行的循环执行的任务,它可以通过以下时间来控制
days(int)
等待天数weeks(int)
等待多少周hours(int)
等待小时数minutes(int)
等待的分钟数seconds(int)
等待的秒数start_date(datetime|str)
触发的开始日期/时间(包含)end_date(datetime|str)
可能触发的最晚日期/时间(包含)timezone(datetime.tzinfo|str)
用于日期/时间计算的时区(默认为调度程序的时区)jitter(int|None)
提前或延迟执行任务的最大随机时间(单位秒)一般比如任务每隔20分钟执行一次,就可以写成
from apscheduler.schedulers.blocking import BlockingScheduler
def interval_task(text):
print(text)
scheduler = BlockingScheduler()
scheduler.add_job(func=interval_task, trigger='interval', minutes=20, args=['text'])
scheduler.start()
一般写个简单的监控程序,监控内存网络等等,inteval
是一个很简单的选择
corn
可以说是最强大的,能实现的计划也是最全面的,主要对以下参数进行配置
year(int|str)
4位数的年份month(int|str)
month (1-12 或者 ‘jan’, ‘feb’, ‘mar’, ‘apr’, ‘may’, ‘jun’, ‘jul’, ‘aug’, ‘sep’, ‘oct’, ‘nov’, ‘dec’)day(int|str)
(1-31) 天week(int|str)
ISO 周 (1-53)day_of_week(int|str)
工作日的编号或名称 (0-6 or 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)
用于日期/时间计算的时区(默认为调度程序的时区)jitter(int|None)
提前或延迟执行任务的最大随机时间(单位秒)其中 year
, month
, day
, week
, day_of_week
, hour
, minute
, second
这些字段是支持表达式配置的
而现在默认支持的表达式格式如下:
表达式 | 描述 |
---|---|
* | 所有值 |
*/a | 从 a 值开始,触发每个 a 值 |
a-b | 对 a-b 范围内的任何值进行触发(a必须小于b) |
a-b/c | 在 a-b 范围内触发每个 c 值 |
xth y | 在一个月中第 x 个 周 y 触发 ( x 支持的参数 1st , 2nd , 3rd , 4th , 5th , last ) |
last x | 在一个月中最后一个 周 x 触发 |
last | 在一个月的最后一天触发 |
x,y,z | 可以用逗号隔开多个表达式 |
看描述就知道这些表达式,并不是满足上面提到的所有字段,这里先不展开了,之后会有一篇关于源码的总结,会介绍这方面的知识
这个触发器主要是以不同的方式结合其他触发器的行为,生成比任何单个触发器更复杂的计划
AndTrigger
始终返回所有给定触发器可以达成一致 最早的下一次触发时间。当任何给定触发器完成其计划时,触发器被视为已完成。OrTrigger
始终返回由任何给定触发器 产生的最早的下一次触发时间。当所有给定触发器都已完成其计划时,触发器将被视为已完成。它们的参数都是一样的
triggers(list)
需要组合的触发器们jitter (int|None)
一个随机数每 2 小时运行一次,但仅在周六和周日运行:job_function
from apscheduler.triggers.combining import AndTrigger
from apscheduler.triggers.interval import IntervalTrigger
from apscheduler.triggers.cron import CronTrigger
trigger = AndTrigger([IntervalTrigger(hours=2),
CronTrigger(day_of_week='sat,sun')])
scheduler.add_job(job_function, trigger)
每周一凌晨 2 点运行,每周二下午 3 点运行:job_function
trigger = OrTrigger([CronTrigger(day_of_week='mon', hour=2),
CronTrigger(day_of_week='tue', hour=15)])
scheduler.add_job(job_function, trigger)
任务存储主要就是存储任务的库, 而 BaseJobStore
是以下几个类的基类,除了初始化存在一点差异,提供的接口基本是一致的,这里主要就介绍基类了
MemoryJobStore
MongoDBJobStore
RedisJobStore
RethinkDBJobStore
SQLAlchemyJobStore
ZooKeeperJobStore
job(Job)
需要添加的任务ConflictingIdError
任务的标识符 job_id
存在相同的时候抛出异常添加一个任务到任务存储中
Return type
list[Job]返回此任务存储中所有任务的列表,返回的任务列表按下一次运行时间(升序)排序。暂停的任务(next_run_time == None)排在最后
now
(datetime.datetime) 当前的日期时间Return type
list[Job]返回早于或等于 next_run_time
的任务列表,返回的任务必须按下一个运行时间(升序)排序
Return type
datetime.datetime返回存储在此任务存储中的所有任务的最早运行时间或者 None(如果没有活动的任务)
job_id(str|unicode)
任务的标识符Return type
Job返回特定的任务,或者 None (找不到)
从该任务存储中删除所有的任务
job_id(str|unicode)
任务的标识符JobLookupError
如果任务不存在从该任务存储中删除指定的任务
释放该任务存储上的所有资源
scheduler
管理这个任务存储的调度程序alias
此任务存储的别名job
要更新的任务JobLookupError
如果任务不存在用给定的较新的版本替换任务存储中的任务
执行器在我们写代码过程中基本不会直接用到,因为我们将执行器参数在配置 schedulers
的时候,就传入了,接下来任务需要执行的时候,schedulers
会根据配置安排执行器执行
BaseExecutor
是以下几个类的基类,而接口主要都在这个基类中定义了,子类负责自己的实现而已
AsyncIOExecutor
DebugExecutor
GeventExecutor
ThreadPoolExecutor
ProcessPoolExecutor
TwistedExecutor
wait(bool)
设置成 True
则等待执行器中所有正在执行的任务结束在停止停止这个执行器
scheduler(apscheduler.schedulers.base.BaseScheduler)
管理这个执行器的调度程序alias(str|unicode)
此执行器的别名当调度程序正在启动或正在将执行器添加到已在运行的调度程序时。
简单点说就在 schedulers
已经启动后,如果我们需要添加新的执行器,需要手动调用 start
来初始化执行器
job(Job)
要执行的工作run_times(list[datetime])
指定应在何时运行任务的日期时间列表MaxInstancesReachedError
如果达到了这个任务能同时运行的最大实例数时抛出的异常提交任务以执行
调度程序统筹了任务存储,任务的触发器和执行器,而 BaseScheduler
是以下几个类的基类,所有这里主要就是介绍基类提供的接口函数
BackgroundScheduler
BlockingScheduler
AsyncIOScheduler
GeventScheduler
TornadoScheduler
TwistedScheduler
初始化调度程序,其中涉及到很多配置参数
logger (str|logging.Logger)
日志输出timezone (str|datetime.tzinfo)
默认时区(默认为本地时区)jobstore_retry_interval (int|float)
从 jobstores
中取任务失败后,重试的间隔job_defaults (dict)
max_instances
: 同一个任务同时允许运行的最大实例,默认为 1, 比如一个耗时10分钟任务,但是5分钟执行一次,如果使用默认值为1, 0分钟的时候启动1个实例,那么5分钟的时候就不会在启动新的实例了,因为已经有1个实例在执行了coalesce
: 当同一个任务因为某些原因导致同时触发时,是否将任务合并成一个任务,默认 False
misfire_grace_time
: 当真正执行任务的时间与计划执行时间的误差,也就是在设置的误差范围内,任务被调用到,该任务还是会被执行,反之则改任务状态就会为 EVENTJOBMISSED
,不执行jobstores (dict)
设置任务商店信息,默认存储为 MemoryJobStore
executors (dict)
设置执行器信息,默认使用 ThreadPoolExecutor
, 也就是线程池的方式,线程池线程数 10
方法 | 描述 | 异常 |
---|---|---|
configure | 对给定的调度程序重新设置配置 | SchedulerAlreadyRunningError –如果调度程序已经在运行 |
start | 启动已配置的执行程序和任务存储,并开始处理计划的任务。 | SchedulerAlreadyRunningError –如果调度程序已经在运行 RuntimeError –如果在禁用线程的uWSGI下运行 |
shutdown | 关闭调度程序及其执行程序和任务存储。 | SchedulerNotRunningError –如果尚未启动调度程序 |
pause | 暂停调度程序中的任务处理。 | |
resume | 在调度程序中恢复任务处理。 | |
wakeup | 通知调度程序可能有任务需要执行。 |
方法 | 描述 | 异常 |
---|---|---|
add_executor | 将执行程序添加到此调度程序。 | ValueError –如果已有给定别名的执行程序 |
remove_executor | 从此调度程序中删除具有给定别名的执行程序。 |
方法 | 描述 | 异常 |
---|---|---|
add_jobstore | 将任务存储添加到此调度程序。 | ValueError –如果已经存在给定别名的任务存储 |
remove_jobstore | 从此调度程序中通过给定别名删除任务存储。 |
方法 | 描述 |
---|---|
add_listener | 添加调度程序事件的侦听器。 |
remove_listener | 删除以前添加的事件侦听器。 |
Job
的配置参数和前面 Job
里介绍的差不多, BaseScheduler
只是将 Job
的接口重新封装了
但是也实现了很多任务是如何添加到任务存储,任务是如何分配给执行器等等实现,所以在 Job
那一部分提到,官方是不希望由用户自己实例化 Job
方法 | 描述 | 异常 |
---|---|---|
add_job | 将给定的任务添加到任务列表中,如果调度程序已经在运行,则将其唤醒。 | |
scheduled_job | 使用 scheduled_job 装饰器来动态装饰 Job 的实际函数 |
|
modify_job | 修改单个任务的属性。 | |
reschedule_job | 为任务构造一个新触发器,并更新其下一个运行时间。 | |
pause_job | 使给定任务在明确恢复之前不执行。 | |
resume_job | 恢复给定任务的计划,如果计划已完成,则将其删除。 | |
get_jobs | 从特定的任务存储或所有任务中返回挂起的任务 | |
get_job | 返回与给定 job_id 匹配的 Job |
|
remove_job | 删除任务,使其无法再运行。 | JobLookupError – 如果没有找到任务 |
remove_all_jobs | 从指定的任务存储中删除所有任务,或者如果没有给出任何任务,则删除所有任务存储。 | |
print_jobs | 打印出当前计划在所有任务存储库或仅特定任务存储库上的所有任务的文本列表。 |
以上部分函数涉及到的形参合返回值我这里就不列出来了,主要是要对每个类大概提供的接口有个印象即可,真正用的时候在查阅官方文档即可,难度不大
https://apscheduler.readthedocs.io/en/stable/py-modindex.html