Иногда нужно сделать так, чтобы скрипт постоянно был активен, ожидая новых задач (в моём случае это рассылка писем).
Скрипт нельзя запустить один раз и надеяться, что он будет работать без сбоев. Поэтому в планировщике задачь (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/.
Комментариев нет:
Отправить комментарий