python 实现定时任务
循环 sleep 方式
- 这种方式实现最简单,在循环里放入要执行的任务,然后 sleep 一段时间在执行。这个方法的缺点是只能在固定的时间间隔下执行,如果有定时任务也无法完成。,n 值需要自己计算,并且 sleep 会阻塞进程的执行,在阻塞过程中程序什么都无法只能等。
import time
from datetime import datetime
# 每 n 秒执行一次
def timer(n):
while True:
print('do something...')
time.sleep(n)
threading 模块的 Timer
- threding 模块中的 Timer 是一个非阻塞函数,程序不会出现阻塞,但是还是无法做到定时执行。
Timer 使用
from threading import Timer
Time(ince, printTime, (time, ))
Timer 函数第一个参数是时间间隔(单位是秒),第二个参数是要调用的函数名,第三个函数是调用函数的参数(tuple)
from threading import Timer
from datetime import datetime
def printTime(time):
print(datetime.now().strftime('%Y-%m-%d'))
time = 3
t = Timer(time, printTime, (time, ))
t.start()
print('over')
输出:
>>> over
>>> 2020-12-31
使用 sched 模块
- sched 模块是 python 内置的模块,它是一个调度(延时处理机制),每次想要定时执行某任务都必须写入一个调度。且程序会出现阻塞仍然无法执行
- sched 用法
- 生成调度器
schedule = sched.scheduler(time.time, time.sleep)
第一个参数是一个可以返回时间戳的函数,第二个参数可以在定时未到达之前阻塞(可以不传递,默认 time.sleep) - 加入调度事件
调度事件:enter、enterables 等,s.enter(param1, param2, param3, param4),四个参数分别为:时间间隔、优先级(用于同时间到达的两个事件同事执行时的顺序)、被调用触发的函数、被调用触发的函数(注意:一定要以 tuple 传入,如果没有参数就 ()) - 运行
s.run(),注意:sched 模块不是循环执行,一次调度被执行完成就结束啦,如果想在执行需再次 enter
import sched
import time
from datetime import datetime
# 初始 sched 模块 schedule 类
schedule = sched.scheduler(time.time, time.sleep)
# 被调用的函数
def printTime():
print(datetime.now().strftime(r'%Y-%m-%d'))
def main(interval):
# enter四个参数分别为:间隔事件、优先级(用于同时间到达的两个事件同时执行时定序)、被调用触发的函数,
schedule.enter(interval, 0, printTime, ())
schedule.run()
main(5)
print('overs')
输出:
>>> 2020-12-31
>>> over
APScheduler 定时框架
APScheduler 是一个python 定时任务框架,使用起来十分方便。提供基于日期、固定时间间隔以及 crontab 类型的任务,并且可以持久化任务、并以 daemon 方式运行应用。
APScheduler 使用
pip install APScheduler
- APScheduler 使用实例
from apscheduler.schedulers.blocking import BlockingScheduler
from datetime import datetime
def printTime():
print(datetime.now().strftime(r'%Y-%m-%d'))
scheduler = BlockingScheduler()
scheduler.add_job(printTime, 'interval', seconds=2)
scheduler.start()
- APScheduler 四个组件
APScheduler 四个组件分别为:触发器(ttigger)、作业存储(job store)、执行器(e xecutor)、调度器(scheduler)
- 触发器(tigger)
包含调度逻辑每一个作业有它租户的触发器,用于决定接下来哪一个作业会运行,除了他们自己初始配置以外触发器完全是无状态的。
APScheduler 有三种内建的 tigger(触发器)
data: 特定时间点触发
interval:固定时间间隔触发
cron:在特定时间周期性触发
- date最基本的一种调度,作业只会执行一次。它的参数如下:
- run_date (datetime|str) – the date/time to run the job at
- timezone (datetime.tzinfo|str) – time zone for run_date if it doesn’t have one already
from datetime import date
from apscheduler.schedulers.blocking import BlockingScheduler
sched = BlockingScheduler()
def my_job(text):
print(text)
# The job will be executed on November 6th, 2009
sched.add_job(my_job, 'date', run_date=date(2009, 11, 6), args=['text'])
sched.add_job(my_job, 'date', run_date=datetime(2009, 11, 6, 16, 30, 5), args=['text'])
sched.add_job(my_job, 'date', run_date='2009-11-06 16:30:05', args=['text'])
# The 'date' trigger and datetime.now() as run_date are implicit
sched.add_job(my_job, args=['text'])
sched.start()
- year (int|str) – 4-digit year
- month (int|str) – month (1-12)
- day (int|str) – day of the (1-31)
- week (int|str) – ISO week (1-53)
- day_of_week (int|str) – number or name of weekday (0-6 or mon,tue,wed,thu,fri,sat,sun)
- hour (int|str) – hour (0-23)
- minute (int|str) – minute (0-59)
- second (int|str) – second (0-59)
- start_date (datetime|str) – earliest possible date/time to trigger on (inclusive)
- end_date (datetime|str) – latest possible date/time to trigger on (inclusive)
- timezone (datetime.tzinfo|str) – time zone to use for the date/time calculations (defaults to scheduler timezone)
表达式:
from apscheduler.schedulers.blocking import BlockingScheduler
def job_function():
print("Hello World")
# BlockingScheduler
sched = BlockingScheduler()
# Schedules job_function to be run on the third Friday
# of June, July, August, November and December at 00:00, 01:00, 02:00 and 03:00
sched.add_job(job_function, 'cron', month='6-8,11-12', day='3rd fri', hour='0-3')
# Runs from Monday to Friday at 5:30 (am) until 2014-05-30 00:00:00
sched.add_job(job_function, 'cron', day_of_week='mon-fri', hour=5, minute=30, end_date='2014-05-30')
sched.start()
参数:
- weeks (int) – number of weeks to wait
- days (int) – number of days to wait
- hours (int) – number of hours to wait
- minutes (int) – number of minutes to wait
- seconds (int) – number of seconds to wait
- start_date (datetime|str) – starting point for the interval calculation
- end_date (datetime|str) – latest possible date/time to trigger on
- timezone (datetime.tzinfo|str) – time zone to use for the date/time calculations
- 作业存储(job store)
存储被调度的作业,默认的作业存储是简单地把作业保存在内存中,其他的作业存储是将作业保存在数据库中,一个作业的数据将保存在持久化作业存储时被序列化,并在加载时被反向序列化,且调度器不能分享同一个作业存储。
APScheduler 默认使用 MemoryJobStore
- 执行器
处理作业的运行,他们通常通过在作业中提交制定的可调用对象到一个线程或者进城池来进行,当作业完成时执行器将会通知调度器。
常用的执行器官有以下两种:
ProcessPoolExecutor
ThreadPoolExecutor
- 调度器
通常在应用中只有一个调度器,应用的开发者通常不会直接处理作业存储、调度器和触发器,相反调度器提供了处理这些合适的接口,配置作业存储和执行器可以在调度器中完成,例如添加、修改、移除作业。
- 配置调度器
APScheduler 提供了许多不同的方式来配置调度器,你可以使用一个配置字典作为参数关键字的方式传入,也可以先创建调度器,再配置和添加作业,这样您可以在不同的环境中得到更大的灵活性。
from apscheduler.schedulers.blocking import BlockingScheduler
from datetime import datetime
def printTime():
print(datetime.now().strftime(r'%Y-%m-%d'))
# 创建 BlockingScheduler
scheduler = BlockingScheduler()
scheduler.add_job(printTime, 'interval', seconds=2)
scheduler.start()
上述代码创建了一个 BlockingScheduler,默认使用默认存储和默认执行器(MemoryJobStore 和 T hreadPoolExecutor,其中线程池最大线程数 <= 10),配置完成使用 start() 启动
- 配置数据库存储
from datetime import datetime
from pymongo import MongoClient
from apscheduler.schedulers.blocking import BlockingScheduler
from apscheduler.jobstores.memory import MemoryJobStore
from apscheduler.jobstores.mongodb import MongoDBJobStore
from apscheduler.executors.pool import ThreadPoolExecutor, ProcessPoolExecutor
# MongoDB 参数
host = '127.0.0.1'
port = 27017
client = MongoClient(host, port)
# 输出时间
def job():
print(datetime.now().strftime("%Y-%m-%d %H:%M:%S"))
# 存储方式
jobstores = {
'mongo': MongoDBJobStore(collection='job', database='test', client=client),
'default': MemoryJobStore()
}
# 执行器
executors = {
'default': ThreadPoolExecutor(10),
'processpool': ProcessPoolExecutor(3)
}
job_defaults = {
'coalesce': False,
'max_instances': 3
}
scheduler = BlockingScheduler(jobstores=jobstores, executors=executors, job_defaults=job_defaults)
scheduler.add_job(job, 'interval', seconds=5, jobstore='mongo')
scheduler.start()
- APSchduler 对 Job 相关操作
- 添加 job
- add_job()
- scheduler_job()
注意⚠️:该方法只适用于应用程序在运行期间不会改变的 job,而 add_job() 返回一个 apscheduler.job.Job实例,可以用来改变或者移除 job
- 移除 job
- remove_job():使用 jobId 移除 job
- job.remove():使用 add_job() 返回的实例
from apscheduler.schedulers.blocking import BlockingScheduler
from datetime import datetime
def printTime():
print(datetime.now().strftime(r'%Y-%m-%d'))
scheduler = BlockingScheduler()
job = scheduler.add_job(printTime, 'interval', seconds=2, id='remove_id')
# 方式一
job.remove()
# 方式二
scheduler.remove_job('remove_id')
- 暂停和恢复 job
- pasue(暂停任务:add_job() 返回的实例 job.pasue())
- resume(恢复任务:add_job() 返回的实例 job.resume())
from apscheduler.schedulers.blocking import BlockingScheduler
from datetime import datetime
def printTime():
print(datetime.now().strftime(r'%Y-%m-%d'))
scheduler = BlockingScheduler()
job = scheduler.add_job(printTime, 'interval', seconds=2, id='remove_id')
# 暂停任务
job.pause()
# 恢复任务
job.resume()
scheduler.start()