APscheduler使用总结

APscheduler是执行定时任务的python库,其作用可以代替Linux系统下的crontab,github上有该库的例子

APsheduler基本使用

该模块由4个基本组件组成:

  • triggers 触发器
  • job stores 任务储存
  • executors 执行器
  • schedulers 调度器

其中triggers定义了定时任务的类别、触发条件以及具体要执行的任务名。
job stores可以将任务储存起来,有些需要重复执行的任务,或有数据生成的任务,有将数据储存的要求,可以通过数据库或文件的形式将数据储存起来以便下次使用。
executors负责具体的任务执行。
schedulers是最高级的组件,负责其它的管理和调度工作,该模块有几种可选的调度器可供选择,使用方法都是一样的:

BlockingScheduler: use when the scheduler is the only thing running in your process
BackgroundScheduler: use then you’re not using any of the frameworks below, and want the scheduler to run in the background inside your application
AsyncIOScheduler: use if your application uses the asyncio module
GeventScheduler: use if your application uses gevent
TornadoScheduler: use if you’re building a Tornado application
TwistedScheduler: use if you’re building a Twisted application
QtScheduler: use if you’re building a Qt application

定时任务的执行内容放在一个函数中使用,而调度的类型和时间等设置则在创建这个job的时候进行定义,在这个模块中,一个具体的任务是一个JOB实例,因此可以通过修改这个实例的属性对任务进行重新设置,而每一个job实例都是依赖于一个scheduler,所以也可以通过sheduler对sheduler进行重新设定。

定时任务的类型说明

参考源码中scheduler.add_job()的参数说明
'cron'跟linux下crontab一样的定时类型写法,
'interval'使用时间间隔的写法
...

说明

from apscheduler.schedulers.background import BackgroundScheduler

# 创建scheduler
scheduler = BackgroundScheduler()

# 创建job
# 方法1
job = scheduler.add_job(myfunc, 'interval', minutes=2, id='my_job_id')
# 方法2
@scheduler.scheduled_job
def myfunc():
    # do something
	pass
	
# 启动
scheduler.start()

# 添加job
scheduler.add_job(myfunc, 'interval', minutes=2, id='my_job_id')
# 删除job
scheduler.remove_job('my_job_id')
# 暂停job
scheduler.pause_job()
# 恢复job
scheduler.resume_job()
# 查看jobs
scheduler.get_jobs()
scheduler.print_jobs()
# 修改job
scheduler.modify_job(max_instances=6, name='Alternate name')
# 重新设置定时任务类型
scheduler.reschedule_job('my_job_id', trigger='cron', minute='*/5')

# 关闭
scheduler.shutdown(wait=False)

scheduler配置

配置内容

  • a MongoDBJobStore named “mongo”
  • an SQLAlchemyJobStore named “default” (using SQLite)
  • a ThreadPoolExecutor named “default”, with a worker count of 20
  • a ProcessPoolExecutor named “processpool”, with a worker count of 5
  • utc标准时区
  • coalescing turned off for new jobs by default
  • 默认最多3个jobs

方法1

from pytz import utc
from apscheduler.schedulers.background import BackgroundScheduler
from apscheduler.jobstores.mongodb import MongoDBJobStore
from apscheduler.jobstores.sqlalchemy import SQLAlchemyJobStore
from apscheduler.executors.pool import ThreadPoolExecutor, ProcessPoolExecutor

jobstores = {
    'mongo': MongoDBJobStore(),
    'default': SQLAlchemyJobStore(url='sqlite:///jobs.sqlite')
}
executors = {
    'default': ThreadPoolExecutor(20),
    'processpool': ProcessPoolExecutor(5)
}
job_defaults = {
    'coalesce': False,
    'max_instances': 3
}
scheduler = BackgroundScheduler(jobstores=jobstores, executors=executors, job_defaults=job_defaults, timezone=utc)

方法2

from apscheduler.schedulers.background import BackgroundScheduler


# The "apscheduler." prefix is hard coded
scheduler = BackgroundScheduler({
    'apscheduler.jobstores.mongo': {
         'type': 'mongodb'
    },
    'apscheduler.jobstores.default': {
        'type': 'sqlalchemy',
        'url': 'sqlite:///jobs.sqlite'
    },
    'apscheduler.executors.default': {
        'class': 'apscheduler.executors.pool:ThreadPoolExecutor',
        'max_workers': '20'
    },
    'apscheduler.executors.processpool': {
        'type': 'processpool',
        'max_workers': '5'
    },
    'apscheduler.job_defaults.coalesce': 'false',
    'apscheduler.job_defaults.max_instances': '3',
    'apscheduler.timezone': 'UTC',
})

方法3

from pytz import utc

from apscheduler.schedulers.background import BackgroundScheduler
from apscheduler.jobstores.sqlalchemy import SQLAlchemyJobStore
from apscheduler.executors.pool import ProcessPoolExecutor


jobstores = {
    'mongo': {'type': 'mongodb'},
    'default': SQLAlchemyJobStore(url='sqlite:///jobs.sqlite')
}
executors = {
    'default': {'type': 'threadpool', 'max_workers': 20},
    'processpool': ProcessPoolExecutor(max_workers=5)
}
job_defaults = {
    'coalesce': False,
    'max_instances': 3
}
scheduler = BackgroundScheduler()

# .. do something else here, maybe add jobs etc.

scheduler.configure(jobstores=jobstores, executors=executors, job_defaults=job_defaults, timezone=utc)

flask下使用APscheduler

flask下使用该模块可以使用flask扩展模块flask-apsheduler

基本使用

from flask import Flask
from flask_apscheduler import APScheduler


class Config(object):
    JOBS = [
        {
            'id': 'job1',
            'func': 'jobs:job1',
            'args': (1, 2),
            'trigger': 'interval',
            'seconds': 10
        }
    ]

    SCHEDULER_API_ENABLED = True


def job1(a, b):
    print(str(a) + ' ' + str(b))

if __name__ == '__main__':
    app = Flask(__name__)
    app.config.from_object(Config())

    scheduler = APScheduler()
    # it is also possible to enable the API directly
    # scheduler.api_enabled = True
    scheduler.init_app(app)
    scheduler.start()
    app.run()

故障排除

在使用gunicorn对flask进行管理的时候如何保证只定时任务只启动一次

问题在使用gunicorn对flask应用进行控制的时候如果设置了gunicorn的--worker参数大于1时,会出现一个定时任务执行多次的问题,此时要给gunicorn提供一个额外的--preload参数,这样,flask的app在run之前的所有操作只会执行一次,因此定时任务就只会执行一次。
env/bin/gunicorn module_containing_app:app -b 0.0.0.0:8080 --workers 3 --preload

flask在调试模式下调用flask-apscheduler定时任务时会执行多次

问题与上面的问题类似,调试模式下只需给app.run()添加一个参数use_reloader即可。
app.run(debug=True, use_reloader=False)