老猫最近作一个小程序开发任务,主要负责后台部分开发;根据项目需求老猫须要实现三个定时任务:数据库
1>定时更新微信token,须要2小时更新一次;
2>商品定时上线;
3>定时检测后台服务是否存活;小程序
老猫使用Python去实现这三个任务,这里须要使用定时相关知识点;
Python实现定点与定时任务方式比较多,老猫找到下面四中实现方式,每一个方式都有本身应用场景;下面老猫来快速介绍Python中经常使用的定时任务实现方式:微信
1>循环+sleep;
2>线程模块中Timer类;
3>schedule模块;
4>定时框架:APScheduler网络
在开始以前先设定一个任务(这样不用依赖外部环境):
1:定时或者定点监测CPU与内存使用率;
2:将时间,CPU,内存使用状况保存到日志文件;并发
先来实现系统监测功能:
准备工做:安装psutil:pip install psutil
功能实现框架
#psutil:获取系统信息模块,能够获取CPU,内存,磁盘等的使用状况 import psutil import time import datetime #logfile:监测信息写入文件 def MonitorSystem(logfile = None): #获取cpu使用状况 cpuper = psutil.cpu_percent() #获取内存使用状况:系统内存大小,使用内存,有效内存,内存使用率 mem = psutil.virtual_memory() #内存使用率 memper = mem.percent #获取当前时间 now = datetime.datetime.now() ts = now.strftime('%Y-%m-%d %H:%M:%S') line = f'{ts} cpu:{cpuper}%, mem:{memper}%' print(line) if logfile: logfile.write(line)
代码运行结果:异步
2019-03-21 14:23:41 cpu:0.6%, mem:77.2%
接下来咱们要实现定时监测,好比3s监测一下系统资源使用状况。ide
这种方式最简单,直接使用while+sleep就能够实现:函数
def loopMonitor(): while True: MonitorSystem() #2s检查一次 time.sleep(3) loopMonitor()
输出结果:oop
2019-03-21 14:28:42 cpu:1.5%, mem:77.6% 2019-03-21 14:28:45 cpu:1.6%, mem:77.6% 2019-03-21 14:28:48 cpu:1.4%, mem:77.6% 2019-03-21 14:28:51 cpu:1.4%, mem:77.6% 2019-03-21 14:28:54 cpu:1.3%, mem:77.6%
这种方式存在问题:只能处理单个定时任务。
又来了新任务:须要每秒监测网络收发字节,代码实现以下:
def MonitorNetWork(logfile = None): #获取网络收信息 netinfo = psutil.net_io_counters() #获取当前时间 now = datetime.datetime.now() ts = now.strftime('%Y-%m-%d %H:%M:%S') line = f'{ts} bytessent={netinfo.bytes_sent}, bytesrecv={netinfo.bytes_recv}' print(line) if logfile: logfile.write(line) MonitorNetWork()
代码执行结果:
2019-03-21 14:47:21 bytessent=169752183, bytesrecv=1107900973
若是咱们同时在while循环中监测两个任务会有等待问题,不能每秒监测网络状况。
timer最基本理解就是定时器,咱们能够启动多个定时任务,这些定时器任务是异步执行,因此不存在等待顺序执行问题。
先来看Timer的基本使用:
导入:from threading import Timer
主要方法:
Timer方法 | 说明 |
---|---|
Timer(interval, function, args=None, kwargs=None) | 建立定时器 |
cancel() | 取消定时器 |
start() | 使用线程方式执行 |
join(self, timeout=None) | 等待线程执行结束 |
定时器只能执行一次,若是须要重复执行,须要从新添加任务;
咱们先来看基本使用:
from threading import Timer #记录当前时间 print(datetime.datetime.now()) #3S执行一次 sTimer = Timer(3, MonitorSystem) #1S执行一次 nTimer = Timer(1, MonitorNetWork) #使用线程方式执行 sTimer.start() nTimer.start() #等待结束 sTimer.join() nTimer.join() #记录结束时间 print(datetime.datetime.now())
输出结果:
2019-03-21 15:13:36.739798 2019-03-21 15:13:37 bytessent=171337324, bytesrecv=1109002349 2019-03-21 15:13:39 cpu:1.4%, mem:93.2% 2019-03-21 15:13:39.745187
能够看到,花费时间为3S,可是咱们想要作的是每秒监控网络状态;如何处理。
Timer只能执行一次,因此执行完成以后须要再次添加任务,咱们对代码进行修改:
from threading import Timer import psutil import time import datetime def MonitorSystem(logfile = None): cpuper = psutil.cpu_percent() mem = psutil.virtual_memory() memper = mem.percent now = datetime.datetime.now() ts = now.strftime('%Y-%m-%d %H:%M:%S') line = f'{ts} cpu:{cpuper}%, mem:{memper}%' print(line) if logfile: logfile.write(line) #启动定时器任务,每三秒执行一次 Timer(3, MonitorSystem).start() def MonitorNetWork(logfile = None): netinfo = psutil.net_io_counters() now = datetime.datetime.now() ts = now.strftime('%Y-%m-%d %H:%M:%S') line = f'{ts} bytessent={netinfo.bytes_sent}, bytesrecv={netinfo.bytes_recv}' print(line) if logfile: logfile.write(line) #启动定时器任务,每秒执行一次 Timer(1, MonitorNetWork).start() MonitorSystem() MonitorNetWork()
执行结果:
2019-03-21 15:18:21 cpu:1.5%, mem:93.2% 2019-03-21 15:18:21 bytessent=171376522, bytesrecv=1109124678 2019-03-21 15:18:22 bytessent=171382215, bytesrecv=1109128294 2019-03-21 15:18:23 bytessent=171384278, bytesrecv=1109129702 2019-03-21 15:18:24 cpu:1.9%, mem:93.2% 2019-03-21 15:18:24 bytessent=171386341, bytesrecv=1109131110 2019-03-21 15:18:25 bytessent=171388527, bytesrecv=1109132600 2019-03-21 15:18:26 bytessent=171390590, bytesrecv=1109134008
从时间中能够看到,这两个任务能够同时进行不存在等待问题。
Timer的实质是使用线程方式去执行任务,每次执行完后会销毁,因此没必要担忧资源问题。
schedule是一个第三方轻量级的任务调度模块,能够按照秒,分,小时,日期或者自定义事件执行时间;
安装方式:
pip install schedule
咱们来看一个例子:
import datetime import schedule import time def func(): now = datetime.datetime.now() ts = now.strftime('%Y-%m-%d %H:%M:%S') print('do func time :',ts) def func2(): now = datetime.datetime.now() ts = now.strftime('%Y-%m-%d %H:%M:%S') print('do func2 time:',ts) def tasklist(): #清空任务 schedule.clear() #建立一个按秒间隔执行任务 schedule.every(1).seconds.do(func) #建立一个按2秒间隔执行任务 schedule.every(2).seconds.do(func2) #执行10S for i in range(10): schedule.run_pending() time.sleep(1) tasklist()
执行结果:
do func time : 2019-03-22 08:51:38 do func2 time: 2019-03-22 08:51:39 do func time : 2019-03-22 08:51:39 do func time : 2019-03-22 08:51:40 do func2 time: 2019-03-22 08:51:41 do func time : 2019-03-22 08:51:41 do func time : 2019-03-22 08:51:42 do func2 time: 2019-03-22 08:51:43 do func time : 2019-03-22 08:51:43 do func time : 2019-03-22 08:51:44 do func2 time: 2019-03-22 08:51:45 do func time : 2019-03-22 08:51:45 do func time : 2019-03-22 08:51:46
执行过程分析:
>1>由于老猫在jupyter下执行,因此先将schedule任务清空; >2>按时间间在schedule中隔添加任务; >3>老猫这里按照秒间隔添加func,按照两秒间隔添加func2; >4>schedule添加任务后,须要查询任务并执行任务; >5>为了防止占用资源,每秒查询到点任务,而后顺序执行;
第5个顺序执行怎么理解,咱们修改func函数,里面添加time.sleep(2)
而后只执行func工做,输出结果:
do func time : 2019-03-22 09:00:59 do func time : 2019-03-22 09:01:02 do func time : 2019-03-22 09:01:05
能够看到时间间隔为3S,为何不是1S?
由于这个按照顺序执行,func休眠2S,循环任务查询休眠1S,因此会存在这个问题。
在咱们使用这种方式执行任务须要注意这种阻塞现象。
咱们看下schedule模块经常使用使用方法:
#schedule.every(1)建立Job, seconds.do(func)按秒间隔查询并执行 schedule.every(1).seconds.do(func) #添加任务按分执行 schedule.every(1).minutes.do(func) #添加任务按天执行 schedule.every(1).days.do(func) #添加任务按周执行 schedule.every().weeks.do(func) #添加任务每周1执行,执行时间为下周一这一时刻时间 schedule.every().monday.do(func) #每周1,1点15开始执行 schedule.every().monday.at("12:00").do(job)
这种方式局限性:若是工做任务回很是耗时就会影响其余任务执行。咱们能够考虑使用并发机制配置这个模块使用。
APScheduler是Python的一个定时任务框架,用于执行周期或者定时任务,
能够基于日期、时间间隔,及相似于Linux上的定时任务crontab类型的定时任务;
该该框架不只能够添加、删除定时任务,还能够将任务存储到数据库中,实现任务的持久化,使用起来很是方便。
安装方式:pip install apscheduler
apscheduler组件及简单说明:
1>triggers(触发器):触发器包含调度逻辑,每个做业有它本身的触发器
2>job stores(做业存储):用来存储被调度的做业,默认的做业存储器是简单地把做业任务保存在内存中,支持存储到MongoDB,Redis数据库中
3> executors(执行器):执行器用来执行定时任务,只是将须要执行的任务放在新的线程或者线程池中运行
4>schedulers(调度器):调度器是将其它部分联系在一块儿,对使用者提供接口,进行任务添加,设置,删除。
来看一个简单例子:
import time from apscheduler.schedulers.blocking import BlockingScheduler def func(): now = datetime.datetime.now() ts = now.strftime('%Y-%m-%d %H:%M:%S') print('do func time :',ts) def func2(): #耗时2S now = datetime.datetime.now() ts = now.strftime('%Y-%m-%d %H:%M:%S') print('do func2 time:',ts) time.sleep(2) def dojob(): #建立调度器:BlockingScheduler scheduler = BlockingScheduler() #添加任务,时间间隔2S scheduler.add_job(func, 'interval', seconds=2, id='test_job1') #添加任务,时间间隔5S scheduler.add_job(func2, 'interval', seconds=3, id='test_job2') scheduler.start() dojob()
输出结果:
do func time : 2019-03-22 10:32:20 do func2 time: 2019-03-22 10:32:21 do func time : 2019-03-22 10:32:22 do func time : 2019-03-22 10:32:24 do func2 time: 2019-03-22 10:32:24 do func time : 2019-03-22 10:32:26
输出结果中能够看到:任务就算是有延时,也不会影响其余任务执行。
APScheduler框架提供丰富接口去实现定时任务,能够去参考官方文档去查看使用方式。
老猫简单总结上面四种定时定点任务实现:1:循环+sleep方式适合简答测试,2:timer能够实现定时任务,可是对定点任务来讲,须要检查当前时间点;3:schedule能够定点定时执行,可是须要在循环中检测任务,并且存在阻塞;4:APScheduler框架更增强大,能够直接在里面添加定点与定时任务;综合考虑,老猫决定使用APScheduler框架,实现简单,只须要直接建立任务,并将添加到调度器中便可。