creating schedule task without Cron job

前端 未结 5 1376
野趣味
野趣味 2020-12-30 17:29

Scheduled task needs to be created but its not possible to use Cron job (there is a warning from hosting provider that \"running the cron Job more than once within a 45-minu

5条回答
  •  再見小時候
    2020-12-30 18:05

    Option A

    An easy way to realize it would be to create a file/database entry containing the execution time of your php script:

     1507979485,
        'sendnewsletter.php' => 1507999485
    ];
    ?>
    

    And on every request made through your visitors you check the current time and if its higher you include your php 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', '')
            // 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);
    ?>
    

    Note: It will be rarily called on the exact second, because you do not know when your visitor will open your page (maybe 2 seconds later). So it makes sense to set the new time for the next day not through adding 86400 seconds. Instead use mktime.

    Option B

    This is a little project I realized in the past, that is similar to @r3wt 's idea, but covers race conditions and works on exact times like a cronjob would do in a scheduler without hitting the max_execution_time. And it works most of the time without the need to resurrect it (as done through visitors in Option A).

    Explanation: The script writes a lock file (to avoid race conditions) for the 15th, 30th, 45th and 60th second of a minute:

    // 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);
        }
    }
    

    Every time a lock file is missing the script creates it and uses sleep() to reach the exact second:

    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);
    

    After waking up again, it sends a request to itself through 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);
    

    By that it calls itself infinitely and you are able to define different starting times:

    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');
        }
    }
    

    Note: It produces 5760 requests per day (4 per minute). Not much, but a cronjob uses much less ressources. If your max_execution_time is high enough you could change it to calling itself only once per minute (1440 requests/day).

提交回复
热议问题