写在前面
假设你在阅读本文时,已经具有了在windows平台安装Python以及Python第三方模块的能力。
Linux平台下面有crontab可以做系统的定时任务, windows下也有对应的定时任务。之前做一个项目,通过windows的计划任务调用php脚本定时给满足条件的用户发送邮件,但是定时任务执行的很不稳定,且针对每个任务都需要做一个单独的定时器,配置比较麻烦,还容易出错。于是就想到了用Python脚本做一个服务程序用来执行定时任务。
做Python定时服务程序首先要解决一下几个问题
1 如何用Python编写windows服务程序?
2 如何定义定时任务?(本文借鉴Linux的crontab,进行定时任务的定义)
3 如何实现通用?(web开发中需要执行定时任务的需求也比较多)
4 如何脱离Python环境单独运行?(用pyinstaller打包exe)
下面从上面4个方面讲述以下我的Python定时服务程序的实现过程,文后附实现的代码。
基于Python的windows 服务程序的编写
用Python来做windows服务程序必须要借助第三方模块pywin32,可以通过pip安装。先上代码,
#!/usr/bin/python
# -*- coding: utf8 -*-
import win32service
import win32serviceutil
import win32event
import servicemanager
import os, sys, time
from smco_croniter import SMCOSched
class CronDaemon(win32serviceutil.ServiceFramework):
# you can NET START/STOP the service by the following name
_svc_name_ = "Cron Daemon"
# this text shows up as the service name in the Service
# Control Manager (SCM)
_svc_display_name_ = "Cron Daemon"
# this text shows up as the description in the SCM
_svc_description_ = "Cron Daemon for scheduled tasks"
def __init__(self, args):
win32serviceutil.ServiceFramework.__init__(self,args)
self.hWaitStop = win32event.CreateEvent(None, 0, 0, None)
self.isAlive = True
def SvcDoRun(self):
while self.isAlive:
print "your code"
time.sleep(10)
def SvcStop(self):
self.ReportServiceStatus(win32service.SERVICE_STOP_PENDING)
win32event.SetEvent(self.hWaitStop)
self.isAlive = False
if __name__ == "__main__":
if len(sys.argv) == 1:
try:
evtsrc_dll = os.path.abspath(servicemanager.__file__)
servicemanager.PrepareToHostSingle(CronDaemon)
servicemanager.Initialize('CronDaemon', evtsrc_dll)
servicemanager.StartServiceCtrlDispatcher()
except win32service.error, details:
if details[0] == winerror.ERROR_FAILED_SERVICE_CONTROLLER_CONNECT:
win32serviceutil.usage()
else:
win32serviceutil.HandleCommandLine(CronDaemon)
用Python实现windows服务程序还是比较简单的,只需要继承win32serviceutil.ServiceFramework 类即可,而我们需要做的就是在继承的类中调用我们需要定时执行的代码。上面的代码在网上有很多,几乎成为了一个模板,这里不再深入的讲述。
如何定义定时任务?
windows上的定时任务配置比较麻烦,而Linux的cron实现的定时任务就比较简单,只需要配置执行的时间和命令即可,基本格式如下(详情请参考,Linux定时任务)
分 时 天 月 周 命令
每分钟执行一次,可以写成, */1 * * * * cmd
每天上午8点执行一次, 可以写成,0 8 */1 * * cmd
因此,我们可以利用linux的cron 定义计划任务。那如何解析cron的计划呢?很幸运,Python已经提供了这个模块,croniter,我们使用 pip install croniter即可安装croniter模块。
至此,就可以利用croniter解析Linux cron格式的计划任务了,而我们只需将计划任务写入到文件中,再有Python读取即可。我定义的计划任务格式如下:
croniter的使用方法如下:
croniter.croniter(sched,basetime)
只需要传入计划任务sched,和计划的开始时间,就可以计算出下一次需要执行的时间,如执行
croniter.croniter("*/5 * * * *","2017-01-04 19:43")
那么计算出的定时任务的下一次执行时间就是,2017-01-04 19:48
如何实现通用?
本文类之间的关系如下图所示,
很惭愧,在我的代码中并没有实现通用,这里我提供一个重构的思路,欢迎大家来一起讨论。
我的思路是将任务抽象成一个接口TaskInterface,以后每添加一个新的定时任务,直接继承TaskInterface,并实现接口中的方法即可。
如何脱离Python环境单独运行?
这个问题比较容易解决,使用PyInstaller,将Python的程序打包成一个exe即可,然后就可以在没有安装Python的环境中运行了。