APScheduler是一个Python作业调度库,必须在应用程序的进程中运行。它能够将Python代码调度为一次或定期运行,主要应用于网站、App等,它不调度OS命令,而是调度Python函数,被认为是进程内的crontab |
一个例子?
默认情况下,APScheduler将所有作业存储在内存中,也可以将这些作业存储在数据库中,保证进程重启时不会丢失作业,也可以恢复到上次的触发状态
根据应用程序的运行方式,它可以作为线程或异步任务运行。初始化实例时,APScheduler不执行任何操作,除非将Python函数作为作业添加,作业一旦添加完成便可启动调度程序
# 安装 APSchedulerrpip install apscheduler# 创建一个文件 app.pyecho >> EOF < app.py>from urllib.request import urlopen>from apscheduler.schedulers.blocking import BlockingScheduler>>scheduler = BlockingScheduler()>>@scheduler.scheduled_job("interval", seconds=10) # 每隔10秒钟请求一次>def keep_warm():> urlopen("https://enqueuezero.com", timeout=10)> >scheduler.start()>EOF
基本概念
Job
Job包含要执行的函数、待传入对的函数参数以及一组调度参数。函数可以是函数对象,也可以是字符串;函数参数对于函数调用至关重要;调度参数用于控制调度器对的行为
def tick(parameter): print(paramter)scheduler.add_job(function, args=(1,), trigger='interval', seconds=3)# tick 即调度函数 function# args 为函数参数# trigger, seconds 为调度参数
Trigger
触发器指示调度程序下一次作业何时运行,所有的任务都有自己的触发器,可以用crontab语法的形式指示触发器
Scheduler
调度器管理所有的事情,可以把它看作是APScheduler提供的一个稳定API,用于配置JonStore、Executors和添加任务。BaseScheduler的扩展类能够在特定环境中运行APScheduler的实例
例如,AsyncIOScheduler允许调度器运行在异步循环中;BackgroundScheduler在线程中运行调度程序等。调度器通常带有自己的执行器,例如,AsyncIOScheduler通过asynexecutor执行作业
JobStore
JobStore存放已调度的作业。没有任何配置的情况下,APScheduler将它们保存在内存中。如上面的代码所示,scheduler.add_job只是将作业保存到内存中,并不会触发函数调用
内存是最简单的解决方案,但是当进程重新启动后,所有对的作业状态都会丢失。当然,也可以选择MongoDB、Redis、Rethinkdb、Zookeeper或SQLAlchemy支持的任何RDBMS(SQLite、MySQL、Postgres等)作为持久化作业存储解决方案
下面的代码,APScheduler添加了一个名为sqlalchemy的JobStore,后面添加的作业选择sqlalchemy作为其JobStore,将作业持久化到SQLite数据库中
scheduler.add_jobstore('sqlalchemy', url='sqlite:sched.db')scheduler.add_job(function, args=(1, ), trigger='interval', seconds=3, jobstore='sqlalchemy')
Executor
执行器管理工作/任务的生命周期,并运行这些作业。默认情况下,可以使用线程或进程作为执行器
OOP
BaseScheduler、BaseExecutor、BaseJobStore和BaseTrigger定义了Schedulers、Executors、JobStores和Triggers的接口。子类为各自的框架实现了这些基类
选择何种调度器、存储器、执行器和触发器取决于用户当前的技术栈,如果这些子类不能满足你的需求,可以按需去扩展这些基类
APScheduler主要类之间的关系图
执行器模式
APScheduler内部有两种主要的作业调度器模式,尽管实现方式不同,但具有相同的接口、提供相同的功能,因此在不知道它如何工作的前提下也能使用
调度器有一个process_jobs方法,触发作业并返回休眠的秒数;另外两个函数sleep或run_after_timeout使调度程序空闲几秒钟
Sleep-Process Model
伪代码如下
wait_seconds = DEFAULTwhile True: sleep(wait_seconds) wait_seconds = process_jobs()
先等待几秒钟,然后处理作业,常见于阻塞式应用程序中(https://github.com/agronholm/apscheduler/blob/master/apscheduler/schedulers/blocking.py)
Callback Model
回调模式是按照回调链约定实现的,伪代码如下
def start(timeout=DEFAULT): run_after_timeout(timeout, wakeup)def wakeup(): timeout = process_jobs() start(timeout)start()
首先等待几秒钟,然后唤醒处理作业,通常有一个事件循环运行,常见于非阻塞式应用程序中(https://github.com/agronholm/apscheduler/blob/master/apscheduler/schedulers/tornado.py)
作业状态
尽管调度器是负责在特定时间执行作业,但也不保证作业一定会被执行,可能的因素有
当前系统负载
当前运行作业数量
如果系统负载很高,调度器没有获得足够的CPU资源,因此有些作业没有机会被触发。建议不要将CPU密集型的任务与APScheduler运行在同一台机器
如果是固定数量的线程池工作,当同时有太多作业并发时,类似于排队有些作业必须等待
生产环境建议设置调度作业数量和延迟,当负载增加时,进行扩容,并将作业分配给不同的机器
Missing Job Executions 由于调度器有时会错过触发作业,APScheduler将是否应该触发超时作业的问题留过终端用户
|----x-------()?------x--------x------|x: job triggered(): job miss triggered?: should the job be triggered afterward?
为解决此问题,APScheduler为每个作业提供一个控制参数misfire_grace_time,如果作业超时,但仍在misfire_grace_time期限内,那么它仍然会被触发。也可以使用coalescing参数将所有为执行的操作合并为一个
Concurrent Job Executions
有时,一个实例正在执行作业,另一个作业也被触发
|----x--------()--------xx?-----x------|x: job triggered(): job miss triggered?: should the two jobs both run?
为解决这个问题,APScheduler为终端用户提供控制参数max_instances
Internal Lock
为支持 misfire_grace_time、coalescing和max_instances,APScheduler投入大量的精力来管理作业状态和它们的运行时
由于作业可能会并发运行,因此APScheduler必须为任何作业获得一个jobstore锁
事件监听器
处理作业管理API之外,APScheduler还为一定数量的事件提供轻量级事件系统。APScheduler在某些情况下触发事件,以便用户代码能够监听它们。下面的示例,说明调度程序如何通过prometheus报告作业错误
def report_error(event): if event.exception: PROM_ERROR_METRICS.inc()scheduler.add_listener(report_error, EVENT_JOB_ERROR)
以上
人工智能知识体系
知识图谱与认知智能
智能运维三部曲
我们谈论AIOps时,聊些什么