python模块推荐[14]定时任务plan

Plan是什么

Plan 是一个python 模块可以帮助我们写和部署cron 任务。借助于plan 可以把python代码转化成cron语法
或者直接修改生成cronfile文件。

plan的优点

  1. 语法对人更加友好。

安装plan

python -m pip install plan

快速体验plan

1
2
3
4
5
6
7
8
from plan import Plan
cron = Plan()
cron.command('date', every='weekend')

if __name__ == '__main__':
cron.run()
```
输出crotab的语法

Begin Plan generated jobs for: main

0 0 6,0 date

End Plan generated jobs for: main

[message] Your crontab file was not updated.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17

此时并没有更新crontab 文件,默认run执行的是check模式不会更新cronfile,只会输出crontab的语法。



## 最佳实践
$ mkdir planschedule
$ cd planschedule
$ plan-quickstart
会生成一个命名为schdule.py的模板文件。


## command
```py
cron.command('top', every='4.hour', output=
dict(stdout='/tmp/top_stdout.log',
stderr='/tmp/top_stderr.log'))

script

1
2
cron.script('script.py', every='1.day', path='/web/yourproject/scripts',
environment={'YOURAPP_ENV': 'production'})

command 和script的区别

command 一般用于linux自带的命令,
而script 用于执行一些脚本,如python perl shell等脚本,会根据脚本文件的后缀名找到对应的解释器。
path 用于定义脚本所在的位置,environment 可以用于定义环境变量,比如指定python的虚拟环境等。

拆分成不同类的任务进行命名

1
2
$ cp schedule.py schedule_commands.py
$ cp schedule.py schedule_scripts.py
1
2
3
4
5
6
7
8
9
10
11
12
from plan import Plan

cron = Plan("commands")

cron.command('top', every='4.hour', output=
dict(stdout='/tmp/top_stdout.log',
stderr='/tmp/top_stderr.log'))
cron.command('yourcommand', every='sunday', at='hour.12 minute.0 minute.30')
# more commands here

if __name__ == "__main__":
cron.run()
1
2
3
4
5
6
7
8
9
10
11
from plan import Plan

cron = Plan("scripts", path='/web/yourproject/scripts',
environment={'YOURAPP_ENV': 'production'})

cron.script('script.py', every='1.day')
cron.script('script_2.py', every='1.month', at='hour.12 minute.0')
# more scripts here

if __name__ == "__main__":
cron.run()

Plan的第一个参数是类别的名字,方便进行控制,比如像删除每一个类别的任务。

Job

Job 中可以包含的参数有 :

  1. task 需要执行的命令
  2. every 执行命令的频率
  3. at 具体时间
  4. path 命令的工作路径
  5. environment 工作环境
  6. output 命令输出到哪里
1
2
3
4
5
from plan import Job

job = Job('ruby script.rb', every='1.month', at='day.5',
path='/web/scripts', output='null',
environment={'RAILS_ENV': 'production'})

Every

Every参数用于定义任务运行的频率:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
[1-60].minute
[1-24].hour
[1-31].day
[1-12].month
jan feb mar apr may jun jul aug sep oct nov dec
and all of those full month names(case insensitive)
sunday, monday, tuesday, wednesday, thursday, friday, saturday
weekday, weekend (case insensitive)
[1].year

"yearly" # Run once a year at midnight on the morning of January 1
"monthly" # Run once a month at midnight on the morning of the first day
# of the month
"weekly" # Run once a week at midnight on Sunday morning
"daily" # Run once a day at midnight
"hourly" # Run once an hour at the beginning of the hour
"reboot" # Run at startup

以及支持原生的crontab语法

1
2
from plan import Job
job = Job('demo', every='1,2 5,6 * * 3,4')

at 语法

定义任务具体运行时间

1
2
3
4
5
6
minute.[0-59]
hour.[0-23]
hour:minute
day.[1-31]
sunday, monday, tuesday, wednesday, thursday, friday, saturday
weekday, weekend (case insensitive)

示例代码:

1
2
3
4
job = Job('onejob', every='1.day', at='12:00')
job = Job('onejob', every='1.day', at='hour.12 minute.15 minute.45')
# or even better
job = Job('onejob', every='1.day', at='12:15 12:45')

PATH

定义运行这个任务需要的工作路径。默认path 是当前脚本的路径。
对于command类型的job, 不依赖于PATH,因此即使定义path 也会被忽略。
For job types that do not need one path, this will be ignored, for example, CommandJob.

output

对任务的输出进行指定位置。
示例代码:

1
2
3
4
job = Job('job', every='1.day', output='null')
job = Job('job', every='1.day', output='> /tmp/stdout.log 2> /tmp/stderr.log')
job = Job('job', every='1.day', output=
dict(stdout='/tmp/stdout.log', stderr='/tmp/stderr.log'))

plan

Plan 对象可以接受的参数有 name, path, environment, output and user。
其中Path, environment 和 output 和Job 中的参数是一样的。
只要在plan中设置了这些参数,在plan中的JOB会自动继承这些参数。

其中environment 可以通过上面dict的形式定义,也可以通过env函数进行定义。

1
2
3
4
cron = Plan()
cron.env('MAILTO', 'user@example.com')
cron.command('command', every='1.day')
cron.run('check')

bootstrap用法

1
2
3
4
5
6
from plan import Plan
cron = Plan()
cron.bootstrap('pip install requests')
cron.bootstrap(['pip install Sphinx', 'sphinx-quickstart'])
cron.script('crawl.py', every='1.day', path='/tmp')
cron.run('check')

bootstrap接受一个命令或者命令列表,bootstrap 和script的区别是仅仅执行一次,不需要定义执行频率。

类别

一个Plan 对象管理一个类别的JOBS。不同的plan对象需要起不同的名字。 根据名字可以对同一类JOBS进行管理。
不同Plan可以方便控制。
在cronfile 中会根据Plan的名字创建相应的block。
示例代码

1
2
3
4
5
6
7
8
9
10
11
12
13
14
from plan import Plan

cron1 = Plan( name="shellscript")
cron1.command('pwd',every='1.day')
cron2 = Plan( name="pythonscript")
cron2.script('1.py',every='1.day')
# register one command, script or module
# cron.command('command', every='1.day')
# cron.script('script.py', path='/web/yourproject/scripts', every='1.month')
# cron.module('calendar', every='feburary', at='day.3')

if __name__ == "__main__":
cron1.run()
cron2.run()

示例输出

1
2
3
4
5
6
7
8
9
10
# Begin Plan generated jobs for: shellscript
0 0 * * * pwd
# End Plan generated jobs for: shellscript

[message] Your crontab file was not updated.
# Begin Plan generated jobs for: pythonscript
0 0 * * * cd /home/czq/planschedule && /home/czq/anaconda2/bin/python 1.py
# End Plan generated jobs for: pythonscript

[message] Your crontab file was not updated.

JOB的类型

plan 内置了4中job的类型: 1. Command Job;2. Script Job; 3. Module Job; 4.Raw Job

raw job的示例代码

1
2
3
4
5
6
cron = Plan()
cron.raw('cd /tmp && ruby script.rb > /dev/null 2>&1', every='1.day')

# In this particular case, you should try Job
job = Job('ruby script.rb', every='1.day', path='/tmp', output='null')
cron.job(job)

自定义JOB类型

plan-quickstart

脚手架工具,帮你产生脚本模板
plan-quickstart –path filepath
plan-quickstart –help

所有的job至少接受两个参数,一个command,一个every

run types

1
2
cron = Plan()
cron.run('check') # could be 'check', 'write', 'update', 'clear'

支持4种不同的run类型,’check’, ‘write’, ‘update’, ‘clear’

  • check cron.run(‘check’) 比较安全,但是不会更新你的crontab文件,会把python code转化成cron语法,
    然后你手动更新crontab文件,crontab -e 默认
  • write 该模式会把你的crontab文件清空,然后写入新的crontab命令到cronfile中。
  • Update 该模式会在cronfile中根据plan对象中task进行更新或者追加新的任务 推荐
  • Clear 该模式会找到plan对象中定义的task,并把它从cronfile文件中删除。

示例代码

from plan import Plan
cron = Plan()
cron.command('sh /home/czq/pymolcn/sub_bd.sh', every='1.day', at='0:05')
cron.command('date', every='weekend')

if __name__ == '__main__':
    cron.run()

参考

  1. https://plan.readthedocs.io/installation.html#installation
  2. https://github.com/fengsp/plan