不使用Cron任务创建定时任务

5

你可以使用一个每60分钟执行一次的cron job,并让它执行60分钟,每分钟触发一次你的请求,或者你可以看看“穷人版cron”的想法,但在低流量页面上不可靠。 - arkascha
5个回答

8
有多种重复性工作的方法。我能想到的其中一些方式是:
  1. 使用:https://www.setcronjob.com/

使用像这样的外部网站在设定的时间间隔内触发您的URL。

  1. 使用meta刷新。更多信息请点击此处。您需要打开页面并保持它运行。

  2. Javascript / Ajax刷新。与上面的示例类似。

  3. 设置cron作业。大多数共享主机都提供了设置cron作业的方式。查看您的托管cPanel。


我知道这已经过去一年了,如果你还在活跃,能否回答一下meta refresh和js/ajax刷新文件是否必须加载才能正常工作,或者它们可以独立地工作而无需实际调用文件? - nothingness
@nothingness 你需要加载页面,以便运行meta/js/ajax。 - Ivanka Todorova
@nothingness 我们曾经在Windows服务器上将它设置为浏览器的主页。你明白吧 - 它是有限制的。 - Santosh Achari

5

如果你拥有 shell 访问权限,你可以通过 shell 执行一个 PHP 脚本。

像这样会是一个无限循环的例子,它会睡眠 60 秒执行,收集垃圾并重复直到永远。

while(true) {
    sleep(60);
    //script here


    //end your script
}

你也可以使用ajax或meta refresh来实现“穷人版cron”。我以前做过这个。基本上,你只需要在脚本开头使用javascript或html的meta refresh添加一个重定向即可。从浏览器中访问此脚本,并保持打开状态。它将每60秒刷新一次,就像cronjob一样。

另一个替代cronjob的选择是bash脚本,如下所示:

#!/bin/bash
while :

do
sleep 60
 wget http://127.0.0.1/path/to/cronjob.php -O Temp --delete-after

done

虽然如此说,你很可能会被主机发现并被终止。

因此,最好的解决方案:

去注册一个每月5-10美元的VPS,告别共享主机,拥抱运行自己的小服务器。

如果你这样做,甚至可以停止使用糟糕的PHP,改用Facebook的HHVM,并享受其出色的性能。


3

http://cron-job.org有一个免费的服务,可以让您设置一个不错的小替代方案。


2
选项A
一种简单的实现方法是创建一个包含您的PHP脚本执行时间的文件/数据库条目:
<?php
// crons.php
return [
    'executebackup.php' => 1507979485,
    'sendnewsletter.php' => 1507999485
];
?>

"对于访客发出的每个请求,您都会检查当前时间,如果时间较晚,则包含您的 PHP 脚本:"
<?php
// cronpixel.php
$crons = @include 'cache/crons.php';
foreach ($crons as $script => $time) {
    if ($time < time()) {
        // create lock to avoid race conditions
        $lock = 'cache/' . md5($script) . '.lock';
        if (file_exists($lock) || !mkdir($lock)) {
            continue;
        }
        // start your php script
        include($script);
        // now update crons.php
        $crons[ $script ] += 86400; // tomorrow
        file_put_contents('cache/crons.php', '<?php return ' . var_export($crons, true) . '; ?' . '>')
        // finally delete lock
        rmdir($lock);
    }
}
header("Last-Modified: " . gmdate("D, d M Y H:i:s") . " GMT");
// image data
$im = imagecreate(1, 1);
$blk = imagecolorallocate($im, 0, 0, 0);
imagecolortransparent($im, $blk);
// image output
header("Content-type: image/gif");
imagegif($im);
// free memory
imagedestroy($im);
?>

注意:它很少会在确切的秒数被调用,因为您不知道访问者何时打开您的页面(也许2秒钟后)。因此,设置下一天的新时间不通过添加86400秒是有意义的。而是使用mktime
选项B: 这是我过去实现的一个小项目,类似于@r3wt的想法,但涵盖了竞态条件,并且像计划程序中的cronjob一样在精确时间上工作,而不会达到max_execution_time。大多数情况下,它可以在无需通过访问者(如选项A)重新启动的情况下运行。
解释: 该脚本为每分钟的第15、30、45和60秒写入锁定文件(以避免竞态条件)。
// cron monitoring
foreach ($allowed_crons as $cron_second) {
    $cron_filename = 'cache/' . $cron_second . '_crnsec_lock';
    // start missing cron requests
    if (!file_exists($cron_filename)) {
        cron_request($cron_second);
    }
    // restart interrupted cron requests
    else if (filemtime($cron_filename) + 90 < time()) {
        rmdir($cron_filename);
        cron_request($cron_second);
    }
}

每次缺少锁文件时,脚本都会创建它并使用sleep()到达确切的秒数:
if (file_exists($cron_filename) || !mkdir($cron_filename)) {
    return;
}
// add one minute if necessary
$date = new DateTime();
$cron_date = new DateTime();
$cron_date->setTime($cron_date->format('H'), $cron_date->format('i'), $sec);
$diff = $date->diff($cron_date);
if ($diff->invert && $diff->s > 0) {
    $cron_date->setTime($cron_date->format('H'), $cron_date->format('i') + 1, $sec);
}
$diff = $date->diff($cron_date);
// we use sleep() as time_sleep_until() starts one second to early (https://bugs.php.net/bug.php?id=69044)
sleep($diff->s);

再次唤醒后,它通过 fopen() 向自身发送请求:
// note: filter_input returns the unchanged SERVER var (http://php.net/manual/de/function.filter-input.php#99124)
// note: filter_var is unsecure (http://www.d-mueller.de/blog/why-url-validation-with-filter_var-might-not-be-a-good-idea/)
$url = 'http' . isSecure() . '://' . filter_input(INPUT_SERVER, 'HTTP_HOST', FILTER_SANITIZE_URL) . htmlspecialchars($request_uri, ENT_QUOTES, 'UTF-8');
$context = stream_context_create(array(
    'http' => array(
        'timeout' => 1.0
    )
));
// note: return "failed to open stream: HTTP request failed!" because timeout < time_sleep_until
if ($fp = @fopen($url, 'r', false, $context)) {
    fclose($fp);
}
rmdir($cron_filename);

通过这种方式,它可以无限地调用自身,您可以定义不同的起始时间:
if (isset($_GET['cron_second'])) {
    if ($cron_second === 0 && !(date('i') % 15)) {
        mycron('every 15 minutes');
    }
    if ($cron_second === 0 && !(date('i') % 60)) {
        mycron('every hour');
    }
}

注意:它每天会产生5760个请求(每分钟4个)。虽然不多,但cronjob使用的资源要少得多。如果您的max_execution_time足够高,您可以将其更改为每分钟仅调用一次(1440个请求/天)。

1
我知道这个问题有点老,但是一周前我偶然发现了它,并且有了同样的问题,我们找到的最佳和安全的选项是使用Web服务。
我们的系统既在共享主机中,也在私人云中。
我们需要一个脚本每月激活一次(计划创建更多的时间表,并允许用户创建一些预定操作)
我们的系统提供对许多客户的访问,因此,当任何人使用系统时,它通过Ajax调用Web服务,并不关心响应(毕竟所有内容都记录在我们的数据库中,必须在没有用户交互的情况下运行)
我们所做的是:
1- 在任何主要屏幕中访问时调用ajax调用。
2- Web服务读取我们数据库上的时间表,并调用任何需要调用的内容。
3- 为了避免许多堆叠的Web服务调用,在实际执行任何操作之前,我们以10分钟的间隔检查日期时间。
这也是一种分配负载平衡的方法,而且时间表不会影响系统与用户交互。

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接