Linux中是利用Crontab来执行系统定时任务,如:
* * * * *
- - - - -
| | | | |
| | | | +----- 星期中星期几 (0 - 6) (星期天 为0)
| | | +---------- 月份 (1 - 12)
| | +--------------- 一个月中的第几天 (1 - 31)
| +-------------------- 小时 (0 - 23)
+------------------------- 分钟 (0 - 59)
我们在开发系统的时候,也需要像Linux一样的定时脚本来方便后台执行我们的程序,如生成统计、汇总报表等等。
例我们在开发考勤系统的时候,就需要每天运行任务脚本来执行任务,生成每月每日报表,下面,我们就来利用Thinkphp和Python开发一个定时任务。
首先,需要一个任务时间检测判断类,来检测当前时间是否有任务可以运行:
<?php
/**
* crontab 时间格式php解析类(PHP 5 >= 5.1.0)
* Author: http://www.daytime.cc
* Linux Crontab格式时间判断
*/
namespace Tasks;
class Crontab {
public $error = '';
public function __construct(){
}
/**
* 检查某时间($time)是否符合某个corntab时间计划($str_cron)
*
* @param int $time 时间戳
* @param string $str_cron corntab的时间计划,如,"30 2 * * 1-5"
*
* @return bool/string 出错返回string(错误信息)
*/
public function check($time, $str_cron) {
$format_time = $this->format_timestamp($time);
$format_cron = $this->format_crontab($str_cron);
if (!is_array($format_cron)) {
return $format_cron;
}
return $this->format_check($format_time, $format_cron);
}
/**
* 使用格式化的数据检查某时间($format_time)是否符合某个corntab时间计划($format_cron)
*
* @param array $format_time $this->format_timestamp()格式化时间戳得到
* @param array $format_cron $this->format_crontab()格式化的时间计划
*
* @return bool
*/
private function format_check(array $format_time, array $format_cron) {
return (!$format_cron[0] || in_array($format_time[0], $format_cron[0]))
&& (!$format_cron[1] || in_array($format_time[1], $format_cron[1]))
&& (!$format_cron[2] || in_array($format_time[2], $format_cron[2]))
&& (!$format_cron[3] || in_array($format_time[3], $format_cron[3]))
&& (!$format_cron[4] || in_array($format_time[4], $format_cron[4]))
;
}
/**
* 格式化时间戳,以便比较
*
* @param int $time 时间戳
*
* @return array
*/
private function format_timestamp($time) {
return explode('-', date('i-G-j-n-w', $time));
}
/**
* 格式化crontab时间设置字符串,用于比较
*
* @param string $str_cron crontab的时间计划字符串,如"15 3 * * *"
*
* @return array/string 正确返回数组,出错返回字符串(错误信息)
*/
private function format_crontab($str_cron) {
//格式检查
$str_cron = trim($str_cron);
$reg = '#^((\*(/\d+)?|((\d+(-\d+)?)(?3)?)(,(?4))*))( (?2)){4}$#';
if (!preg_match($reg, $str_cron)) {
$this->error = '格式错误';
return false;
}
try{
//分别解析分、时、日、月、周
$arr_cron = array();
$parts = explode(' ', $str_cron);
$arr_cron[0] = $this->parse_cron_part($parts[0], 0, 59);//分
$arr_cron[1] = $this->parse_cron_part($parts[1], 0, 59);//时
$arr_cron[2] = $this->parse_cron_part($parts[2], 1, 31);//日
$arr_cron[3] = $this->parse_cron_part($parts[3], 1, 12);//月
$arr_cron[4] = $this->parse_cron_part($parts[4], 0, 6);//周(0周日)
} catch (Exception $e) {
$this->error = $e->getMessage();
return false;
}
return $arr_cron;
}
/**
* 解析crontab时间计划里一个部分(分、时、日、月、周)的取值列表
* @param string $part 时间计划里的一个部分,被空格分隔后的一个部分
* @param int $f_min 此部分的最小取值
* @param int $f_max 此部分的最大取值
*
* @return array 若为空数组则表示可任意取值
* @throws Exception
*/
private function parse_cron_part($part, $f_min, $f_max) {
$list = array();
//处理"," -- 列表
if (false !== strpos($part, ',')) {
$arr = explode(',', $part);
foreach ($arr as $v) {
$tmp = $this->parse_cron_part($v, $f_min, $f_max);
$list = array_merge($list, $tmp);
}
return $list;
}
//处理"/" -- 间隔
$tmp = explode('/', $part);
$part = $tmp[0];
$step = isset($tmp[1]) ? $tmp[1] : 1;
//处理"-" -- 范围
if (false !== strpos($part, '-')) {
list($min, $max) = explode('-', $part);
if ($min > $max) {
$this->error = '使用"-"设置范围时,左不能大于右';
return false;
}
} elseif ('*' == $part) {
$min = $f_min;
$max = $f_max;
} else {//数字
$min = $max = $part;
}
//空数组表示可以任意值
if ($min==$f_min && $max==$f_max && $step==1) {
return $list;
}
//越界判断
if ($min < $f_min || $max > $f_max) {
$this->error = '数值越界。应该:分0-59,时0-59,日1-31,月1-12,周0-6';
return false;
}
return $max-$min>$step ? range($min, $max, $step) : array((int)$min);
}
}
然后在执行任务时,通过调用类检测当前任务是否可以运行:
$crontab = new Crontab();
$tasktime = time();
foreach($tasks as $task){
if($crontab->check($tasktime, $task['runtime'])){
// 执行任务内容
}
}
最后,我们通过Thinkphp生成一个URL接口供Python定时调用,如每隔一分钟或10秒钟调用一次接口
如:http://www.daytime.cc/tasks/pytask/run?taskid=任务ID
到此,大功告成,你也可以像Linux Crontab一样优雅地运行系统任务了。
最终效果如下图所示:
THE END