Иногда нужно сделать так, чтобы скрипт постоянно был активен, ожидая новых задач (в моём случае это рассылка писем).
Скрипт нельзя запустить один раз и надеяться, что он будет работать без сбоев. Поэтому в планировщике задачь (cron) устанавливается интервал на ежеминутный запуск (или реже, в зависимости от требований), а все проверки остаются на совести программы.
Для того чтобы скрипт мог проверять сам себя (запущен ли процесс, не завис ли), я использую proc-файл - временный файл создаваемый скриптом. Мой proc-файл содержит только одно число - PID (ID процесса), записанное туда при первом запуске программы. Также из этого файла можно узнать время его модификации, что даёт возможность прервать запущенный процесс, если он выполняется слишком долго.
Я уже давно использую этот подход, но теперь, когда тоже самое потребовалось сделать под Windows - я решил написать для этого отдельный класс.
Класс для работы с proc-файлом
class ProcFile { private static $file_name = 'php_job.proc'; private static $file_ttl = 1200; // (sec) after what time the process considered "hanging" public static function setName($file_name_) { self::$file_name = $file_name_; } // check if process is already ON public static function isCurrent() { $proc_file = self::getPath(); if (!file_exists($proc_file)) return false; $pid = intval( file_get_contents($proc_file) ); if (!self::checkIfRunning($pid)) return false; // if process is hanging - it should be stoped if ((time() - filemtime($proc_file)) > self::$file_ttl) { self::killProcess($pid); return false; } return true; } // creates / overwrites proc-file public static function put() { file_put_contents(self::getPath(), getmypid()); } // updates file modification time public static function update() { touch(self::getPath()); } // proc-file full path private static function getPath() { return sys_get_temp_dir() . self::$file_name; } private static function checkIfRunning($pid) { $os_name = php_uname('s'); $found = false; switch ($os_name) { case 'Windows NT': $processes = explode("\n", shell_exec('tasklist.exe')); foreach ($processes as $process) { if (!strlen($process) OR !preg_match('/(.*?)(\d+).*$/', $process, $matches)) continue; if ($matches[2] == $pid) { $found = true; break; } } break; case 'FreeBSD': $output = shell_exec('ps ax | grep \'^[[:space:]]*' . $pid . '\''); $found = (bool) strlen($output); break; default: trigger_error('Unknown OS "' . $os_name . '" in "' . __METHOD__ . '"'); } return $found; } private static function killProcess($pid) { $os_name = php_uname('s'); $killed = false; switch ($os_name) { case 'Windows NT': shell_exec('taskkill /F /PID ' . $pid); $killed = true; break; case 'FreeBSD': shell_exec('kill ' . $pid); $killed = true; break; default: trigger_error('Unknown OS "' . $os_name . '" in "' . __METHOD__ . '"'); } if ($killed) trigger_error('Previous sender instance is taking too long, killing...; ' . __FILE__ . ', ' . __LINE__, E_USER_NOTICE); } }
Принцип работы
// Указать имя файла ProcFile::setName('mail_send.proc'); // Проверить текущее состояния proc-файла, и действовать соотвественно if (ProcFile::isCurrent()) { echo 'let the previous instance to work' . NL; exit; } else { echo 'start anew' . NL; ProcFile::put(); } while (true) { $res = db_query(...); while ($row = db_fetch($res)) { // работа скрипта // обновить время модификации proc-файла ProcFile::update(); } // обновить время модификации proc-файла ProcFile::update();
Как запускать скрипт в Windows
Можно создать ярлык вида:
"C:\Program Files\PHP\php.exe" -f с:\htdocs\newsletters\send.php
и поместить его в папку Control Panel -> Scheduled Tasks. Затем два раза кликнув по ярлыку, можно настроить время запуска. Но проблема в том, что при запуске скрипта будет появляться окно cmd.exe, и я не нашёл способа от него избавиться.
Есть ещё вариант запускать скрипт как Windows-сервис, но это отдельная тема.
В моём случае скрипты на Windows-сервере запускаются с помощью сторонней программы подобия cron.
Как запускать скрипт в FreeBSD / Linux
Тут процесс отработанный. В crontab записывается строка вида:
*/1 * * * * user php -f /usr/script/send.php
, и скрипт запускается каждую минуту. За более полной информацией можно обратиться в документацию - http://crontab.org/.
Комментариев нет:
Отправить комментарий