【问题标题】:Scheduling php scripts调度php脚本
【发布时间】:2011-07-28 10:56:13
【问题描述】:

我想创建一些函数来安排 php 脚本,例如,如果我想在 12/12/2012 12:12 运行 page.php,我可以调用

schedule_script('12/12/2012 12:12','page.php');//or by passing a time/datetime object

或者例如每分钟调用一个脚本

schedule_interval(60,'page.php');//every 60s=1minute

我可能会添加一些其他功能来查看安排了哪些脚本或删除其中一个。

我希望这个函数在 UNIX 和 WINDOWS 平台上都可以工作,我不想要丑陋的解决方案,比如在站点的每个页面上执行一个脚本(我想在站点上没有人时安排这个命令)或使用“buisy”等待”实现(在检查是否有任何计划作业的脚本上使用 sleep())或需要用户干预的东西(比如在控制台中写东西或打开面板)。

我在 MSDOS 上找到了“AT”命令(适用于所有窗口)但它非常基本,因为它只接受时间而不接受日期,在 UNIX 上有一个更强大的版本但我不知道如何使用它(我想要两个平台的解决方案)。

【问题讨论】:

  • crons 呢?这就是他们的目的。 Windows 也有类似的东西。
  • 据我所知,它在windows下被称为“任务”。
  • 适用于 unix 的 Cron 作业并在 Windows 中安排任务。然后由您决定脚本应该做什么。
  • 我认为他的意思是他希望能够从 PHP 脚本中调用此命令,它应该为他创建一个 cron 作业或 Windows 计划任务。
  • 至少对于 *nix 系统来说,使用 php 脚本设置 cron 作业是没有问题的。我可以想象它在windows下类似。

标签: php cron scheduling


【解决方案1】:

有一个 PHP 函数可以让您将脚本执行延迟到某个时间点。

假设我有cron.php:

<?php

   // Usage:
   //    cron.php [interval|schedule] [script] [interval|stamp]
   if(!isset($argc) || count($argc)!=2)die; // security precaution

   $time=(int)$argv[3]; // just in case :)

   if($argv[1]=='schedule'){
       time_sleep_until((int)$_GET['until']);
       include_once($time);
   }elseif($argv[1]=='interval')
       while(true){ // this is actually an infinite loop (you didn't ask for an "until" date? can be arranged tho)
           usleep($time*1000); // earlier I said milliseconds: 1000msec is 1s, but this func is for microseconds: 1s = 1000000us
           include_once($argv[2]);
       }

?>

还有你的classes/functions 文件:

// Const form K2F - Are we on windows?
define('ISWIN', strpos(strtolower(php_uname()),'win')!==false &&
                strpos(strtolower(php_uname()),'darwin')===false );

// Function from K2F - runs a shell command without waiting (works on all OSes)
function run($cmd){
    ISWIN ? pclose(popen('start /B '.$cmd,'r')) : exec($cmd.' > /dev/null &');
}

script_schedule($script,$time){
    if(is_string($time))$time=strtotime($time);
    run('php -f -- schedule '.escapeshellarg($script).' '.$time);
}

script_interval($script,$mseconds){
    run('php -f -- interval '.escapeshellarg($script).' '.$mseconds);
}

它应该工作。顺便说一句,K2F 就是这个框架,可以让你的梦想成真……更快。 ;)。干杯。

编辑:如果您仍然想要关于计算正在运行的作业和/或删除(停止)它们的部分,我也可以帮助您。只需回复我的帖子,我们会跟进。

【讨论】:

  • 这就像一个“忙碌的等待”,但这不是一个坏主意。无论如何,我已经创建了一个类似的解决方案,创建了一个 MUTEX(它在脚本之间工作,即使在 Windows 上也不会锁定(你仍然可以选择让它等待),就像 php 中的廉价羊群一样)并且它可以从错误中恢复。我创建了一个调度程序脚本,一个函数 run_script() 可以启动任何脚本,如 exec 但无需等待。调度程序只需在启动时锁定互斥锁,如果它被锁定则退出(所以我只有一个调度程序处于活动状态)并每 60 秒循环一次以检查作业数据库并启动所有准备好的作业。我只需启动一次时间表。
  • 我稍后会发布我的解决方案,您的解决方案很好,但有一个问题:如果服务器重新启动,所有作业都将停止,并且对于每个预定的作业,php 必须打开一个“线程”。在我的解决方案中,我只有一个线程,您只需检查锁是否处于非活动状态,如果它处于非活动状态,您只需启动调度程序。无论如何,如果有人有更好的想法,请发布。
  • 如果服务器重新启动,在您的情况下,所有作业也必须重新启动。至于线程,如果您为每个作业加载一个(不同的)PHP 文件,则无论您喜欢与否,都需要有一个不同的线程。我不完全理解您使用互斥锁的解决方案,因为这主要是一种确保运行一次同时能够与之通信的方法——我在代码中忽略了这一点,因为我没有将它视为您的主要范围问题。
  • 你的代码是完美的,我会用它的一部分来改进我的脚本,无论如何我的想法是有一个像 CRON 一样工作的单一计划过程,检查是否有一些工作准备好并启动它们,所以我计划中只有一个进程空闲(它检查(并执行)并休眠 60 秒)。我以这种方式实施它是因为我计划安排大量工作,否则您的实施会很棒。如果有人只能使用系统命令(如果不是安全/权限问题),请发布。
【解决方案2】:

这是我的调度程序的实现,我只需要在不活动时启动它并在 mysql 作业表上添加所有作业(我的主脚本已经有一个),这个脚本将启动所有准备好的作业被执行(sql表有一个日期时间字段)。

我所说的“互斥锁”是一个类,它告诉脚本的一个或多个副本是否正在运行,甚至可以通过管道向正在运行的脚本发送命令(您只需创建一个具有相同所有脚本的名称),因此您甚至可以从另一个脚本停止正在运行的脚本。

<?php
//---logging---
$logfile = dirname(dirname(__FILE__)).'/scheduler.log';
$ob_file = fopen($logfile,'a');
function ob_file_callback($buffer)
{
  global $ob_file;
  fwrite($ob_file,$buffer);
}




//--includes---

  $inc=dirname(dirname(__FILE__)).'/.include/';
  require_once($inc.'Mutex.php');
  require_once($inc.'jobdb.php');
//--mutex---
//i call it mutex but it's nothing like POSIX mutex,it's a way to synchronyze scripts
  $m=new Mutex('jscheduler');
  if(!$m->lock())//if this script is already running
    exit();//only one scheduler at time
//---check loop---
  set_time_limit(-1);//remove script time limit
  for(;;){
      ob_start('ob_file_callback');//logging 

     $j=jobdb_get_ready_jobs(true);//gets all ready jobs,works with mysql
     if($j!=null)//found some jobs
         foreach($j as $val){//run all jobs

            $ex='*SCRIPT NAME AND PARAMETERS HERE*';

            if(!run_script($ex))
                echo "UNABLE TO LAUNCH THE JOB!\n";
        }
     $n=($j!=null)?count($j).'JOBS LAUNCHED':'NO JOBS';


     sleep(60);
     if($m->has_to_stop())//yeah,i can stop this script from other scripts,it works with a file pipeline
        exit("# STOPPING SCHEDULER\n");            

     ob_end_flush();//LOGGING  
  }

?>

我的“run_script”函数与 Sciberras“run”函数的工作方式相同。

要激活调度程序,您只需使用此命令

 run_script('scheduler.php');

检查它是否处于活动状态

$m=new Mutex('jscheduler');
if(!$m->test_lock())
    echo 'SCHEDULER IS ACTIVE';
else 
    echo 'SCHEDULER IS INACTIVE';

并停止调度程序

$m=new Mutex('jscheduler'); 
$m->ask_to_stop();//simply sent throught the pipe the command,now has_to_stop()will return true on the process that have gain the lock of the mutex
echo 'STOPPING SCHEDULER...';

我可能在他的实现上走得太远了,无论如何可能存在“滞后”问题,例如,如果调度程序从 0:00.00 开始,而我在 0:01.01 和 0:01.59 有两个脚本都是于 0:02.0 启动。 为了解决这个“滞后”,我可能会检索下一分钟安排的所有作业,并使用 time_sleep_until 在 Sciberras 代码中安排它们。 这不会创建太多正在运行的线程(我可能想测试在启动大量线程时是否存在限制或性能下降,但我相信会有一些问题)并确保完美的时间安排只需要检查是否调度程序处于活动状态。

【讨论】:

    【解决方案3】:
    $amt_time = "1";
    $incr_time = "day";
    $date = "";
    $now=  ''. date('Y-m-d') ."";   
    if (($amt_time!=='0') && ($incr_time!=='0')) {
    $date = strtotime(date("Y-m-d".strtotime($date))."+$amt_time $incr_time");
    $startdate=''.date('Y-m-d',$date) ."";
    } else {
    $startdate="0000-00-00";
    }
    if ($now == $startdate) {
    include ('file.php');
    }
    

    只是一个猜测;)实际上我可能有相反的想法,但你明白了

    【讨论】:

    • 请使用我在示例中使用的函数格式,而且已知脚本非常重,所以我不能使用包含
    猜你喜欢
    • 2012-07-26
    • 1970-01-01
    • 2014-08-04
    • 2015-05-06
    • 2015-05-26
    • 1970-01-01
    • 2011-11-30
    • 2013-09-26
    • 1970-01-01
    相关资源
    最近更新 更多