如何使用PHP每分钟运行一次cron

Linux操作系统中cron作业条目的经典配置文件如下:

# ┌─────────────  (0 - 59)
# │ ┌─────────────  (0 - 23)
# │ │ ┌─────────────  (1 - 31)
# │ │ │ ┌─────────────  (1 - 12)
# │ │ │ │ ┌─────────────   (0 - 6)
# │ │ │ │ │
# * * * * * <  >

前五个参数指示此任务的执行时间,第六个参数-必须运行的命令本身。时间参数是:分钟,小时,天,月和星期几。此外,所有数字都必须以整数或特殊语法形式表示

结果,启动命令的最小可能间隔是每分钟一次。

对于许多任务,更频繁地需要执行命令,例如,每10秒执行一次。对于某些业务流程自动化任务,最大允许延迟通常不超过1-1.5秒。

当然,经典的cron不适合此-需要改进。

以下是在Linux上为经典cron创建附加功能(使用PHP)并使用防止重启进程的附加保护的分步实现。

任务设置和cron设置


例如,我们将使用以下任务:

  1. 在前端,用户可以通过单击“运行”按钮来启动某些复杂任务的执行。
  2. 将新行写入数据库后,后端将通知用户。
  3. 通过cron,我们将“跟踪”这些新任务并尽快完成它们,以使用户不会在一分钟内得到结果,而会立即*。

*如果仅每分钟使用一次命令启动,则任务将在秒数达到最接近的零(新的分钟的开始)时开始。因此,在经典形式下,用户将需要等待0到59秒的执行时间。

因此,cron将以其最大形式进行配置,即 每分钟一次:

* * * * * /usr/bin/php -q /run.php > /dev/null 2>&1
#/usr/bin/php -    PHP   (      )
#/run.php -   PHP   
#> /dev/null 2>&1 - ,            run.php

单周期


最初,您应该确定我们将在数据库中请求新任务的频率-依赖于此,周期数和逻辑睡眠(函数sleep将发生变化,

在当前示例中,将使用10秒的步长。因此,循环数为60/10 =6。总计,通用代码如下:

for ($cycle = 1; $cycle <= 6; $cycle++) { # 6 
    $all_tasks = get_all_tasks(); #     
    if ($all_tasks) { #    - 
        foreach($all_tasks as $one_task) { #     
            solve_one_task($one_task); #  
        }
    }
    sleep(10); #  ,  ""  10 
}

澄清:在此示例中,使用了10秒的步长,这可以提供每10秒一次的最小脚本执行间隔。因此,为了更频繁地执行,您应该更改周期数和“睡眠时间”。

避免重复执行任务


在呈现的形式中,存在一种不确定性,即,如果任务已经开始,则重复执行该任务。如果任务“困难”并且需要几秒钟的时间来执行,则尤其如此。

这会导致重新执行问题:

  • 该功能solve_one_task()已在运行,但尚未完成其工作;
  • 因此,在数据库中,任务仍被标记为未完成;
  • 下一个周期将再次获得该任务,并solve_one_task()使用相同的任务再次运行该功能

当然,这可以解决,例如,通过为此任务更改数据库中的某些状态。

但是我们不会加载数据库:根据我的测试,MYSQL可以接受请求,但不能立即处理它。相差0.5秒可能会导致重复执行-这绝对不适合。

同样在这种情况下,我们仅讨论任务的状态,因此最好使用服务器的文件系统。

基本验证模型是使用flock-设置和解锁文件的功能构建的

在PHP执行中,该函数可以表示如下:

$lock_file_abs = 'file'; #  
$fp = fopen($lock_file_abs,"w+"); #   
if (flock($fp, LOCK_EX | LOCK_NB)) { #,    
    solve_one_task($one_task); #  
    flock($fp, LOCK_UN); #   ,     
}
else {
    #,   , ..       
}
fclose($fp); #   
unlink($lock_file_abs); # ,   

结果


整个周期的总体视图如下:

for ($cycle = 1; $cycle <= 6; $cycle++) {
    $all_tasks = get_all_tasks();
    if ($all_tasks) {
        foreach($all_tasks as $one_task) {
            $lock_file_abs = __DIR__.'/locks/run_'.$one_task['id'];
            $fp = fopen($lock_file_abs,"w+");
            if (flock($fp, LOCK_EX | LOCK_NB)) {
                solve_one_task($one_task);
                flock($fp, LOCK_UN);
            }
            else {
                #    
            }
            fclose($fp);
            unlink($lock_file_abs);
        }
    }
    sleep(10);
}

因此,这种算法使您可以开始任务处理的循环执行,而不必担心任务将被处理多次。

现在,仅需每分钟通过cron运行此代码就足够了,从而又将在其内部运行较小的周期。

All Articles