python之少不了的定时任务

今天我们使用的apscheduler ,需要环境依赖的安装
pip install apscheduler

apscheduler的四大组件

  1. triggers 触发器 可以按照日期、时间间隔或者 contab 表达式三种方式触发
  2. job stores 作业存储器 指定作业存放的位置,默认保存在内存,也可以保存在各种数据库中
  3. executors 执行器 将指定的作业提交到线程池或者进程池中运行
  4. schedulers 作业调度器 常用的有 BackgroundScheduler(后台运行)和 BlockingScheduler (阻塞式)

代码实践

import time
from apscheduler.schedulers.background import BlockingScheduler
from apscheduler.triggers.interval import IntervalTrigger


def my_job():
    print('my_job, {}'.format(time.ctime()))


if __name__ == "__main__":
    scheduler = BlockingScheduler()

    # 间隔设置为1秒,还可以使用minutes、hours、days、weeks等
    intervalTrigger=IntervalTrigger(seconds=1)

    # 给作业设个id,方便作业的后续操作,暂停、取消等
    scheduler.add_job(my_job, intervalTrigger, id='my_job_id')
    scheduler.start()
    print('=== end. ===')

案例2

# -*- coding: utf-8 -*-
import os
from datetime import datetime
from apscheduler.schedulers.blocking import BlockingScheduler


def tick():
    print('Tick! The time is: %s' % datetime.now())


if __name__ == '__main__':
    scheduler = BlockingScheduler()
    scheduler.add_job(tick, 'interval', seconds=3)
    print('Press Ctrl+{0} to exit'.format('Break' if os.name == 'nt' else 'C    '))

    try:
        scheduler.start()
    except (KeyboardInterrupt, SystemExit):
        pass

案例3

from datetime import datetime
import os
from apscheduler.schedulers.blocking import BlockingScheduler


def tick():
    print('Tick! The time is: %s' % datetime.now())


if __name__ == '__main__':
    scheduler = BlockingScheduler()
    # 每天8点40执行任务
    scheduler.add_job(tick, 'cron', hour=18, minute=40)
    print('Press Ctrl+{0} to exit'.format('Break' if os.name == 'nt' else 'C    '))

    try:
        scheduler.start()
    except (KeyboardInterrupt, SystemExit):
        pass

# minute = '*/3'  # 表示每5分钟执行一次
# hour = '19-21', minute = '23'  # 表示19:23, 20:23, 21:23 各执行一次任务

效果如下,可以看到没有执行 end,为什么呢?请看下面解释:
在这里插入图片描述
因为我们使用了 BlockingScheduler,它是阻塞式的,所以只看到了 my_job 方法中的输出,而语句 print(’=== end. ===’) 并没有被执行。BackgroundScheduler 它可以在后台运行,不会阻塞主线程的执行,来看下面的代码

import time
from apscheduler.schedulers.background import BackgroundScheduler
from apscheduler.triggers.interval import IntervalTrigger


def my_job():
    print('my_job, {}'.format(time.ctime()))


if __name__ == "__main__":
    scheduler = BackgroundScheduler()
    intervalTrigger=IntervalTrigger(seconds=1)
    scheduler.add_job(my_job, intervalTrigger, id='my_job_id')
    scheduler.start()
    print('=== end. ===')
    while True:
        time.sleep(1)

在这里插入图片描述
如果把 triggers 设置成 DateTrigger,就变成作业在某一个时间点执行,示例如下,等到设定的时间到了,作业就会被自行一次

import time
import datetime
from apscheduler.schedulers.background import BlockingScheduler
from apscheduler.triggers.date import DateTrigger


def my_job():
    print('my_job, {}'.format(time.ctime()))


if __name__ == "__main__":
    scheduler = BlockingScheduler()
    intervalTrigger=DateTrigger(run_date='2020-07-17 16:18:55')
    scheduler.add_job(my_job, intervalTrigger, id='my_job_id')
    scheduler.start()

如果想按照指定的周期去执行的话,就需要使用 CronTrigger 了,工作原理跟 UNIX 中 crontab 定时任务非常相似,它可以指定非常详细且复杂的规则。同样的来看示例代码

import time
from apscheduler.schedulers.background import BlockingScheduler
from apscheduler.triggers.cron import CronTrigger


def my_job():
    print('my_job, {}'.format(time.ctime()))


if __name__ == "__main__":
    scheduler = BlockingScheduler()
    
    # 第一秒执行作业
    intervalTrigger=CronTrigger(second=1)

    # 每天的19:30:01执行作业
    # intervalTrigger=CronTrigger(hour=19, minute=30, second=1)

    # 每年的10119点执行作业
    # intervalTrigger=CronTrigger(month=10, day=1, hour=19)

    scheduler.add_job(my_job, intervalTrigger, id='my_job_id')
    scheduler.start()

第一秒执行==一分钟
在这里插入图片描述
下面我们看看 executors 的使用

import time
from apscheduler.schedulers.background import BlockingScheduler
from apscheduler.triggers.interval import IntervalTrigger
from apscheduler.executors.pool import ThreadPoolExecutor


def my_job():
    print('my_job, {}'.format(time.ctime()))


if __name__ == "__main__":
	#可以看到,我们将线程池的大小改为了20,在初始化 scheduler 的时候将 executors 传递进去,后面的操作就跟之前的完全一样了
    executors = {
        'default': ThreadPoolExecutor(20)
    }

    scheduler = BlockingScheduler(executors=executors)

    intervalTrigger=IntervalTrigger(seconds=1)
    scheduler.add_job(my_job, intervalTrigger, id='my_job_id')
    scheduler.start()

关于 ThreadPoolExecutor 和 ProcessPoolExecutor 的选择问题,这里有一个原则,如果是 cpu 密集型的作业,使用 ProcessPoolExecutor,其它的使用 ThreadPoolExecutor,当然 ThreadPoolExecutor 和 ProcessPoolExecutor 也是可以混用的
最后我们来看看作业存储器,我们把它改成存储到 sqlite 中

import time
from apscheduler.schedulers.background import BlockingScheduler
from apscheduler.triggers.interval import IntervalTrigger
from apscheduler.executors.pool import ThreadPoolExecutor
from apscheduler.jobstores.sqlalchemy import SQLAlchemyJobStore


def my_job():
    print('my_job, {}'.format(time.ctime()))

jobstores = {
    'default': SQLAlchemyJobStore(url='sqlite:///jobs.sqlite')
}


if __name__ == "__main__":

    executors = {
        'default': ThreadPoolExecutor(20)
    }

    scheduler = BlockingScheduler(jobstores=jobstores, executors=executors)

    intervalTrigger=IntervalTrigger(seconds=1)
    scheduler.add_job(my_job, intervalTrigger, id='my_job_id')
    scheduler.start()

代码执行后,会在源码目录下生成 sqlite 数据库文件 jobs.sqlite,我们使用图形化工具打开查看,可以看到数据库中存放的作业的 id 和作业下次执行的时间
在这里插入图片描述
时间间隔如下:

weeks (int) – 间隔几周 
days (int) – 间隔几天 
hours (int) – 间隔几小时 
minutes (int) – 间隔几分钟 
seconds (int) – 间隔多少秒 
start_date (datetime|str) – 开始日期 
end_date (datetime|str) – 结束日期 
timezone (datetime.tzinfo|str) – 时区 

举例说:

from apscheduler.schedulers.blocking import BlockingScheduler
from apscheduler.events import EVENT_JOB_EXECUTED, EVENT_JOB_ERROR
import datetime
import logging

logging.basicConfig(level=logging.INFO,
                    format='%(asctime)s %(filename)s[line:%(lineno)d] %(levelname)s %(message)s',
                    datefmt='%Y-%m-%d %H:%M:%S',
                    filename='log1.txt',
                    filemode='a')


def aps_test(x):
    print(datetime.datetime.now().strftime('%Y-%m-%d %H:%M:%S'), x)


def date_test(x):
    print(datetime.datetime.now().strftime('%Y-%m-%d %H:%M:%S'), x)
    print(1/0)


def my_listener(event):
    if event.exception:
        print('任务出错了!!!!!!')
    else:
        print('任务照常运行...')


scheduler = BlockingScheduler()
scheduler.add_job(func=date_test, args=('一次性任务,会出错',), next_run_time=datetime.datetime.now() + datetime.timedelta(seconds=15), id='date_task')
scheduler.add_job(func=aps_test, args=('循环任务',), trigger='interval', seconds=3, id='interval_task')
scheduler.add_listener(my_listener, EVENT_JOB_EXECUTED | EVENT_JOB_ERROR)
scheduler._logger = logging
scheduler.start() 

# 特别说明id的用法
job = scheduler.add_job(myfunc, 'interval', minutes=2)
job.remove()
#如果有多个任务序列的话可以给每个任务设置ID号,可以根据ID号选择清除对象,且remove放到start前才有效
sched.add_job(myfunc, 'interval', minutes=2, id='my_job_id')
sched.remove_job('my_job_id')

使用数据库作为作业存储器

from apscheduler.schedulers.blocking import BlockingScheduler
import datetime
from apscheduler.jobstores.memory import MemoryJobStore
from apscheduler.executors.pool import ThreadPoolExecutor, ProcessPoolExecutor
from apscheduler.jobstores.sqlalchemy import SQLAlchemyJobStore
def my_job(id='my_job'):
    print (id,'-->',datetime.datetime.now())
jobstores = {
    'default': SQLAlchemyJobStore(url='sqlite:///jobs.sqlite')
}
executors = {
    'default': ThreadPoolExecutor(20),
    'processpool': ProcessPoolExecutor(10)
}
job_defaults = {
    'coalesce': False,
    'max_instances': 3
}
scheduler = BlockingScheduler(jobstores=jobstores, executors=executors, job_defaults=job_defaults)
scheduler.add_job(my_job, args=['job_interval',],id='job_interval',trigger='interval', seconds=5,replace_existing=True)
scheduler.add_job(my_job, args=['job_cron',],id='job_cron',trigger='cron',month='4-8,11-12',hour='7-11', second='*/10',\
                  end_date='2018-05-30')
scheduler.add_job(my_job, args=['job_once_now',],id='job_once_now')
scheduler.add_job(my_job, args=['job_date_once',],id='job_date_once',trigger='date',run_date='2018-04-05 07:48:05')
try:
    scheduler.start()
except SystemExit:
    print('exit')
    exit()  

未完待续…


  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值