За последние 24 часа нас посетили 35335 программистов и 1814 роботов. Сейчас ищут 857 программистов ...

Запуск ресурсоемкой задачи

Тема в разделе "PHP для новичков", создана пользователем freelsd, 23 янв 2016.

  1. freelsd

    freelsd Активный пользователь

    С нами с:
    12 апр 2015
    Сообщения:
    63
    Симпатии:
    0
    В общем, такая ситуация - нужно из интерфейса запустить скрипт (например через exec() или system()), который будет производить расчеты а в это время часть интерфейса будет недоступна, но состояние работы скрипта должно как-то выводится полльзователю. Я такого раньше не реализовывал, поэтому идея такая:

    1)Запускаем скрипт через system (только как потом перенаправить пользователя на главную страницу, ведь этот вызов, вроде как, блокирующий)
    2)Сделать запись в бд, что идет работа и часть интерфейса показываться не будет (смотрим в бд, если есть запись о том что работа идет, то не показываем некоторые меню)
    3)Но как от скрипта получать результаты в реальном времени? Например, чтобы аяксом каждые 30 секунд производились запросы к скрипту и он возвращал какие-то данные (но так не получится, потому что скрипт работать будет, значит нужен какой-то промежуточный?)
     
  2. Ganzal

    Ganzal Суперстар
    Команда форума Модератор

    С нами с:
    15 мар 2007
    Сообщения:
    9.893
    Симпатии:
    965
    технология скорборда.
    запустили сценарий, придумали произвольную строку, вернули её клиенту и перенаправили клиента к другой странице - где он статус будет смотреть, запрашивая с этой произвольной.
    продолжительная программа на определенных этапах записывает данные в оговоренное место.
    скрипт статуса читает данные из этого оговоренного места и показывает пользователю в дружелюбном формате.

    оговоренное место может быть обычным текстовым файлом, бинарным файлом, пхп-файлом, таблица в субд, таблица в ноускуэль решении вроде рэдиса или мемкеша.
     
  3. freelsd

    freelsd Активный пользователь

    С нами с:
    12 апр 2015
    Сообщения:
    63
    Симпатии:
    0
    Спасибо. Я хотел бы уточнить некоторые детали. Если скрипт обращается к БД, то есть смысл через какой-то промежуток к ней обращаться. То есть, скрипт сделал какой-то процент от работы и записал в БД - сделал столько-то процентов. Так? Да и как собственно перенаправление клиента сделать чтобы процесс работал в фоне, например запущенный через system. Или можно каким-то другим образом это сделать?
     
  4. artoodetoo

    artoodetoo Суперстар
    Команда форума Модератор

    С нами с:
    11 июн 2010
    Сообщения:
    11.128
    Симпатии:
    1.248
    Адрес:
    там-сям
    Можно не буквально system запускать (такую возможность лучше вообще закрыть), а выкладывать каким-то образом "задание". Думай об этом как об обработке очереди. Один процесс пихает задания в очередь, другой обрабатывает.

    Если оставаться в рамках PHP, обработчиком может быть скрипт, запускаемый кроном или какой-нибудь phpdaemon.
     
  5. freelsd

    freelsd Активный пользователь

    С нами с:
    12 апр 2015
    Сообщения:
    63
    Симпатии:
    0
    Мне нужно запустить обработку сразу же, как пользователь загрузит файл. Нуи потом результат как-то отображать.
     
  6. Ganzal

    Ganzal Суперстар
    Команда форума Модератор

    С нами с:
    15 мар 2007
    Сообщения:
    9.893
    Симпатии:
    965
    напрмер

    страница start.php
    Код (PHP):
    1. <?php
    2. // создаем айдишник сессии
    3. $session_id = md5(microtime(true));
    4.  
    5. // стартуем с ним нашу прогу
    6. // амперсанд отстёгивает новый процесс от текущего, чтоб не ждать завершения
    7. exec("/usr/bin/php /path/to/program.php {$session_id} &");
    8.  
    9. // редиректим юзера к сценарию статуса
    10. header("Location: /status.php?session_id={$session_id}");
    11. ?>
    страница status.php
    Код (PHP):
    1. <?php
    2. // проверяем наличие идентификатора сессии
    3. if (!isset($_GET['session_id']))
    4. {
    5.   // ой потерялся
    6.   die('session_id required');
    7. }
    8.  
    9. // формируем имя файла
    10. $sb_file = '/tmp/program-session-' . $_GET['session_id'] . '.php';
    11.  
    12. // проверяем наличие
    13. if (!file_exists($sb_file))
    14. {
    15.   // еще одна потеря
    16.   die('session not found');
    17. }
    18.  
    19. // загружаем
    20. $sb = include $sb_file;
    21.  
    22. // показываем страницу
    23. ?><!DOCTYPE html>
    24. <html>
    25. <head>
    26. <title>Program status. Session: <?=$_GET['session_id']?></title>
    27.  
    28.  
    29. <meta http-equiv="refresh" content="5">
    30.  
    31. </head>
    32. <body><h1>Program</h1>
    33. <p><strong>launched</strong> <?=$sb['launched']?></p>
    34. <p><strong>progress</strong> <?=$sb['progress']?>%</p>
    35. </body></html>
    программа program.php
    Код (PHP):
    1. <?php 
    2. // проверим передан ли аргумент сессии
    3. if (empty($argv[1]))
    4. {
    5.   // ах
    6.   echo "session_id required";
    7.   exit 1;
    8. }
    9.  
    10. // наше время запуска
    11. $launched = date('Y-m-d H:i:s');
    12. // наш файл
    13. $sb_file = '/tmp/program-session-' . $argv[1] . '.php';
    14.  
    15. // наш длинный цикл
    16. for ($i = 0; $i <= 100; $i++)
    17. {
    18.   // кладем текущее состояние в файл
    19.   file_put_contents($sb_file, sprintf('<?php return %s', var_export(['launched' => $launched, 'progress' => $i], true)));
    20.  
    21.   // спим секунду-другую
    22.   sleep(rand(1, 5));
    23. }
    24.  
    25. // спасибо
    26. ?>
     
  7. freelsd

    freelsd Активный пользователь

    С нами с:
    12 апр 2015
    Сообщения:
    63
    Симпатии:
    0
    Спасибо, я посмтрю. Но разве exec не блокирует скрипт до конца работы?
     
  8. Ganzal

    Ganzal Суперстар
    Команда форума Модератор

    С нами с:
    15 мар 2007
    Сообщения:
    9.893
    Симпатии:
    965
    ну да, амперсанд забыл в конец строки заткнуть для детача процесса.
     
  9. freelsd

    freelsd Активный пользователь

    С нами с:
    12 апр 2015
    Сообщения:
    63
    Симпатии:
    0
    А можно ли как-то узнать, нормально ли вызов произошел или при старте программы, например ошибки были?
     
  10. Ganzal

    Ganzal Суперстар
    Команда форума Модератор

    С нами с:
    15 мар 2007
    Сообщения:
    9.893
    Симпатии:
    965
    все можно. перенаправление ввода, статусы, сигналы, форки. мы в разделе для новичков, запрашиваемая информация для очень подготовленного ума. изначальная задача была как проследить за выполнением долгого скрипта - вот те минимальное рабочее решение. тупо покажет от нуля до сотни. ты с ним разобрался?
     
  11. freelsd

    freelsd Активный пользователь

    С нами с:
    12 апр 2015
    Сообщения:
    63
    Симпатии:
    0
    Ну почти. Свой скрипт у меня получилось запустить, правда не совсем как хотелось бы.
    Код (PHP):
    1. $cmd = "start /B  \"php test.php > C:\\users\\alex\\desktop\\123.txt\""; 
    2. pclose(popen($cmd,'w')); 
    Во-первых вывод в файл не перенаправляется, а во-вторых я хз как узнать запустился ли скрипт или какие ошибки посыпались.

    Подсказка от модератора:
    Любой код или текст конфигурации пишите между тегом [code=php] и [/code].
    Используйте отступы в коде для форматирования текста.
    Это помогает быстрее понять вас, увеличивает шанс на получение ответа.
    Что выделять? Например: PHP, HTML, CSS, JavaScript, SQL, XML, .htaccess, ini, регулярные выражения, код шаблонизаторов, любая другая разметка, результаты array/object dump и т. д.
     
  12. Ganzal

    Ganzal Суперстар
    Команда форума Модератор

    С нами с:
    15 мар 2007
    Сообщения:
    9.893
    Симпатии:
    965
    может быть полный путь? может странная конструкция открытия-закрытия процесса? и где перенаправление вывода ошибок?
     
  13. freelsd

    freelsd Активный пользователь

    С нами с:
    12 апр 2015
    Сообщения:
    63
    Симпатии:
    0
    Путь к рнр у меня в системных переменных. Все отрабатывает, только в файл не перенаправляется. И по popen тоже определить, нормально ли запуск произошел, нельзя.
     
  14. Ganzal

    Ganzal Суперстар
    Команда форума Модератор

    С нами с:
    15 мар 2007
    Сообщения:
    9.893
    Симпатии:
    965
    а почему попен а не экзек или систем или пасфру? есть вообще понимание как работать с ресурсом открытым попен-ом?
    а вывод ошибок можно отправить в файл или в другой поток. у тебя же стандартный вывод заворачивается в файл - вот таким же раком и другие дескрипторы перехватываются:

    2>&1 - вывод ошибок в стандартный вывод
    2>/path/to/file - вывод ошибок в файл или другую программу
     
  15. freelsd

    freelsd Активный пользователь

    С нами с:
    12 апр 2015
    Сообщения:
    63
    Симпатии:
    0
    Попеном я хотел определять удачно ли завершился вызов или нет. Но он false не возращает даже если файл запускаемый не существует. И какая конкретно причина фейла тоже не узнать. Так тоже не работает:
    Код (PHP):
    1. $cmd = "start php test.php 2>&1 1> C:\\users\\alex\\desktop\\123.txt"; //комманда
    2. pclose(popen($cmd,'w')); 
     
  16. Ganzal

    Ganzal Суперстар
    Команда форума Модератор

    С нами с:
    15 мар 2007
    Сообщения:
    9.893
    Симпатии:
    965
    для определения удачности есть статус. рэтвал.
    для определения ошибок существует стандартный поток ошибок. стдэрр. дескриптор два.
    попеном открывается ресурс с которым ты потом интерактивно общаешься. в стдин пишешь из стдаута читаешь. в стдэре ждешь предупреждения. в третьем и далее дескрипторах ждешь что-то еще если ты их открыл и ими пользуешься.
     
  17. freelsd

    freelsd Активный пользователь

    С нами с:
    12 апр 2015
    Сообщения:
    63
    Симпатии:
    0
    Я понимаю, но можно простой пример, который запустит процесс фоном и в случае неудачи запуска (отсутсует пхп файл, например) сделает вывод какой-нибудь? Я разные способы пробовал, но либо процесс начинает висеть и не идет в фон, либо не выводятся ошибки.
     
  18. Ganzal

    Ganzal Суперстар
    Команда форума Модератор

    С нами с:
    15 мар 2007
    Сообщения:
    9.893
    Симпатии:
    965
    хватит оверквотить.

    Код (PHP):
    1. exec('/usr/bin/php /path/to/file.php 2>/tmp/stderr.log >/tmp/stdout.log &');
    с виндой как-нибудь сам.
     
  19. freelsd

    freelsd Активный пользователь

    С нами с:
    12 апр 2015
    Сообщения:
    63
    Симпатии:
    0