php 的 计划任务——Crontab

Crontab Linux/UNIX 系统上最常用 的计划任务 理工具 PHP 开发者在实际项目中
会经常使用到该工具。不过受限于实际的项目部署应用环境, Crontab 置信息的编辑多
由专门的服务器维护人员操作,项目使用者想要自行添加和编辑,流程较为烦琐。
本章将会基于内容管理框架,开发 个可视化的 Crontab 计划任务管理模块,极大地
方便了开发者和用户的使用。

1 常见计划任务实现万法
在实际开发中,经常会遇见有计划任务需求的场景,如创建但未支付的订单,或失败
需要退款的商品等,这些都需要定时执行某些操作。类似的场景还有许多。本节主要讲解
常见的几种计划任务的实现方法。

1.1 PHP 脚本实现计划任务
因为 PHP 脚本是基于浏览器运行的,在关闭浏览器后程序会自动终止,同时程序执
行的最大时间默认为 秒,所以要让 PHP 脚本实现计划任务,就需要一个或多个脚本,
不依赖浏览器的动作而可以一直执行。下面给出常见方法。

脚本执行时间限制:使用 PHP 内置的 set_time_limit()方法可以实现让脚本持续执行。
set_time_limit(0); //通过 set me limit (0 )可以让程序无限制地执行下去

浏览器关闭,程序不终止:使用 PHP 内置的 ignore_user_ abort()方法,可以让脚本在关闭浏览器的情况下可以继续执行。
ignore_user_abort(); //关闭浏览器 PHP 脚本也可以继续执行

脚本定时执行 为了不让 PHP 提前结束进程,使用语言内置的无限循环结构和
sleep()等待方法可以让脚本定时执行,如 5秒执行1次。
sleep($interval); //延时执行

这里给出一个简单的脚本实例,演示 PHP 计划任务的实现过程。新建 task.php 本文
件,实现代码如下:

<?php 
ignore_user_abort(); //关闭浏览器 PHP 脚本也可以继续执行
set_time_limit(0); //通过 set me limit (0 )可以让程序无限制地执行下去
$interval = 3 ; //每隔3秒运行
$number = 0; //计数器
 
//无限循环
do{ 
($number < 10) {
    //执行 次文件写入操作
    //定时写入文件
    file_put_contents( _DIR_.'/log/log'.date('Ymd_His').'.log',time()) ; 
    $number =$number+1 ; 
    sleep($interval); //延时执行
 
}while(true) ; 

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
在上述代码中,实现了循环内的代码每 秒执行 次, 共执行 10 次的计划任务(新
log 文件) 。注意脚本头文件中定义的两个系统方法,代码如下:

ignore_user_abort(); //关闭浏览器 PHP 脚本也可以继续执行
set_time_limit(0); //通过 set me limit (0 )可以让程序无限制地执行下去
1
2
在浏览器中访问 task .php 脚本,可以发现在 lo 目录下,会自动新增 10 log 文件,

不过使用这种方式,若更新了 PHP 文件内容,需要 重启PHP进程才可以使计划任务
生效。 此外, 若出现程序错误或者内存溢出, PHP 当前运行的进程也会被终止,故使用这
种方式具有极大的不稳定性,维护起来并不方便。

1.2 使用系统级别的计划任务工具
既然使用 PHP 脚本实现计划任务,在一些方面不是很理想,那么借助外部的工具就
是更好的选择。无论是windows 还是 Linux 操作系统,都有完 的计划任务解决方案
如在 Windows 10 中, 在控制面板中,就可以直接找到任务计划程序井管理计划任务,使
用起来较为简单,

不过基于 PHP 项目, 一般部署在windows 服务器上的较少,故 PHP 开发者最常用
的还是 Linux系统中的 Crontab 计划任务命令工具 为了方便演示,这里使用 vagrant 工具,
在本地搭建 Ubun 的虚拟机系统,登录成功后 执行以下命令查看 Crontab 帮助信息。

crontab -h
1
结果如下

也可以执行以下命令,查看管理员用户( root )有哪些计 任务。

crontab -l
1
执行结果如下

其实无论使用哪个计划任务工具,都是为了实现在特定的时间执行某项任务这样的功

2.Crontab 入门
Linux 上的 Crontab 计划任务工具 没有可视化的交互界面 配置与使用该工具都需
要在 命令行运作, 本节主要讲解 Cron tab 工具的 些入 操作。

2.1 Crontab 使用教程
在讲之前之前, 需要先了解 Cron 和 Crontab 的区别。经常使用到的 Crontab 命令
是Cron和 Table 合体的简写 它其实是 Cron 配置文件,也可以 作业列 开发者可
以在 定义不同的时间表达式 实现不同的任务需求。
而 Cron是Linux 系统进程,Cron 搭配 Shell 脚本,就可以执行特定的计划任务。

以ubun 16.04 为例 Cron tab 在系统中多个地方都有配置文件。

/var/spool/cron/crontabs:该目录下存放的是每个用户的 Crontab ,配置文件的 与用户名一 致。
/etc/cron.d/: 存放需要执行的 Crontab 文件或脚本。
/etc/cron.hourly、 /etc/cron.daily 、/etc/cron.weekly 、/etc/cron。monthly:这些目录中的脚本,可以以每小时、每天、每星期和每月为单位执行1次。
常用的命 明如下:
crontab [- u username) //省略用户表表示操作当前用户的 Cron tab

e 编辑任务列表)
1 列出任务列表里的命令)
r 删除任务列表)
Crontab 的命令构成为:时间表达式+操作符+操作命令。时间表达式的构成有分、时、
日、月、周 5种, 操作符有以下几种:

*:取值范围内的所有数字
/:每过多少个数字
-:从x到z
.:散列数字 例如:每分钟执行一次命令 `showtima` 实例如下: `* * * * * showtima`
1
2
3
4
每天 30 执行某个 PHP 脚本文件 示例如下

 30 23 * * * /usr/bin/php7.0 /var/www/task.php
1
时间表达式的 义非常灵活 不过若不经常使用,极易导致配置 这里 推荐使用
在线的 Crontab 式生成器 ,根据需求进行定制。访问地址为:http://www pppet.net
访问后使用效果


2.2 Crontab 实现 PHP 文件定时写入
为了方便演示,这里实现1个每分钟写入文件日期的功能 首先在系统的/var/www
(Ubun 虚拟机环境)目录下,新增 log.php 文件 体代码如下

<?php 
file_put_contents(__DIR__.'/run.log',date("Y-m-d H:i:s")."\r\n",FILE_APPEND);  
?>
1
2
3
此脚本每次执行,都会生成 个带有当前时间信息的文件,以便区分文件是何时创建
随后执行以下命令,给当前用户增加 Cronta 表达式,指定何时执行 log.php 脚本

crontab -e
1
增加以下内容:

* * * * *  /use/bin/php7.0  /var/www/log.php
1
保存后等待几分钟,发现在/var/www 目录下,自动生成了 run.log 文件,使用 cat
令查看,内 容如下

root@scotchbox:/var/www# cat run.log
2018-01-03 16:01:01 
2018-03-03 16:02:01 
2018-03-03 16:03:02  
1
2
3
4
根据 Crontab 中配置的时间任务表达式,log.php脚本每隔一分钟都会被执行一次,而
此时修改脚本文件,则不需要重启 PHP 进程,修改的代码如下:

<?php 
file_put_contents(__DIR__.'/run.log',"time:".date("Y-m-d H:i:s")."\r\n",FILE_APPEND);  
?>
1
2
3
经过几分钟的等待,再次查看 ru log 文件,发现己经写入了新的格式内容

2018-01-03 16:01:01 
2018-03-03 16:02:01 
2018-03-03 16:03:02 
2018-01-03 16:04:01 
time:2018 01-03 16 : 05 : 01
1
2
3
4
5
通过对日志列表细心观 可以发现, Crontab 在执行的时候,默认最小单位为“分”,
并且会从第1 秒(根据配置文件编辑的时间,误差在2秒内)开始执行。开发者还可以通
过使用 sleep 关键字定义在哪1秒执行,修改时间表达式如下:

* * * * * sleep 20; /use/bin/php7.0  /var/www/log.php
1
sleep 后面的参数 20 指的就是延迟多少秒执行, 等待几分钟后,再次查看 run.log 文件,
发现文件写入的时间发生了变化:

2018-03-03 16:03:02 
2018-01-03 16:04:01 
time:2018 01-03 16:05:01
time:2018 01-03 16:06:01
time:2018 01-03 16:07:01
time:2018 01-03 16:08: 01
time:2018 01-03 16:10: 21
time:2018 01-03 16:11: 21
1
2
3
4
5
6
7
8
奇奇怪怪的知识
完成计划任务显示列表管理的开发后,用户就可以 自行定义需要的任务内容了,不过想
要自动运行,还需要 个自动执行任 的脚本,用来理这些任务。其实 步骤如下。

1. 安装第三方依赖类库

为了提升开发效率,这里主要安装两个依赖类库,用来解析 Crontab 表达式和发送
HTTP 请求。在项目的根目录下,安装 mtdowling/cron- expression 的命令如下:

composer require mtdowling/cron- expression 
1
为了更好地获取请求 URL 地址的 HTTP 回值 安装使用 rmccue/requests 的命令
如下

composer require rmccue/requests
1
2.开发核心任务脚本
为了更好地管理代码,在项目 application 目录下,新增 crontab 应用模块,在此模块
下的 controller 目录下,新增 Crontask.php 控制器文件,在此文件内开发任务执行脚本,
其步骤如下。
(1)新增初始化方法。为了防止误操作 ,此脚本只能在命令行下操作,在 Crontask 控制器文件中,增加以下代码

/*
*初始化配置列表
@var array 
*/
private $_config = [] ;
/*
*初始化方法
*/
public function _initialize(} {
    parent::_initialize();
    config('app_trace', false) ; //关闭app_trace
    //只可以以 cli 方式执行
    if(!$this->request- >isCli()){
        $this->error("计划任务必须在命令行中执行");
    }
    //初始化状态
    $this->_config=[
    'DELETE'=>-1,//己经删除
    'DISABLED'=>0,//禁用
    'NORMAL'=>1,//正常
    'COMPLETED'=>2,//完成
    'EXPIRED'=>3,//过期
    
    ]
}
 
 
 

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
为了脚本稳定地执行,代码强制关闭了框架自带的 Trace 输出模式,并禁止在非命令
行模式下运行此脚本的任何方法。其代码如下:


    config('app_trace', false) ; //关闭app_trace
if(!$this->request- >isCli()){
        $this->error("计划任务必须在命令行中执行");
    }
1
2
3
4
5
(2)实现任务管理核心方法。在控制器新增index()方法,增加以下代码

/**
*定时任务执行方法
*@return bool 
**/
public function index(){
    //查询所有的任务列表
    $map['status']= ['>', 0]; 
    $crontab_list = Db::name("crontab ")->where ($map)->select();
    if(!$crontab_list){
            return false;
    }
    $now_time =time();
    foreach($crontab_list as $key =>$cromtab){
        $is_execute = false ; //是否执行
        $update =[];//需要更新的数据
         if($now_time<$cromtab["begin_time"]){
             continue;//任务没开始跳过
         } 
         if($cromtab["maximums"] &&$cromtab["executes"]>=$cromtab["maximums"]){
                 $update["status "]=$this->config["COMPLETED"];// 任务超过最大执行次数 任务完成 
         }elseif($cromtab["end_time"] >0 && $now_time> $cromtab["end_time"] ){
 
             $update["status "]=$this->config["EXPIRED"];// 任务过期
         }else{
                //创建计划任务对象并传入时 表达式
                $cron = CronExpression::factory($crontab[' schedule']); 
                if($cron->isDue()){
                    //允许执行
                    $is_execute = true ; 
                    //允许执行的时候更新状态
                    $update["execute_time"]= $now_time; 
                    $update["update_time"]= $now_time;
                    $update["executes"]=$crontab["executes"]+ 1;
                     $update["status "]= ($crontab["maximums "]>0 &&$update["executes"]>=$update["maximums "]?$this->config["COMPLETED"]:$this->config["NORMAL"];
                     )else{
                         //如果未到 行时间 跳过本任务去判断下一个任务
                         continue; 
                     }
                }
                  $map= [] ; 
                     $map["id"]= $crontab["id"];
                     Db::name("crontab")-> where($map)->update($update);
                     //通过标志位来判断是否满足执行条件(任务是否开始、是否未达到规定次数等)最终决定执行任务还是只更新任务状态
                     if(!$is_execute){
                         continue;
                     }else{

//执行计划任务操作
try{ 
        //判断任务类型
        switch($crontab['type']){
            //请求 URL
            case "url"
                if(substr($crontab["content"] ,0,1)=="/"){
                    //本地url
                $request =    shell_exec("php".__ROOT_PATH__."index.php".$crontab["content"]."2>&1");
                //修改日志
                
                }else{
                    //远程 URL使用curl请求
                        //修改日志
                    
                }
                break;
            case "shell"
                $request =    shell_exec($crontab["content"]."2>&1");
                    //修改日志
                    break;
        
        }
        }catch(\Execeptionm $e){
                记录异常

        }
 
    }
}
————————————————
版权声明:本文为CSDN博主「极梦网络无忧」的原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接及本声明。
原文链接:https://blog.csdn.net/wangzhae/article/details/115019692

猜你喜欢

转载自blog.csdn.net/qq_31432773/article/details/130724451