Есть скрипт, который долго выполняется - парсинг большого количества xml-страниц. Через Х секунд сервер хостера вырубает его с ошибкой 500. Подскажите подходы к организации подобных работ? 1. Логика подсказывает, что надо делить работу скрипта на этапы, так чтобы каждый этап был короче, чем тайм-аут сервера, записывать данные и затем повторять с точки остановки. Как это реализовать? 2. Читал про exec(). Значит, если у меня есть script1.php, то я могу указать в нем Код (Text): exec('script2.php'); Верно? 3. Тогда вопрос: как передать в script2.php информацию о точке, на которой я остановился? На ум приходит прицепить к ссылке параметр Код (Text): <a href = ""> script2.php?point = <?php echo $var; ?> </a> В эту $var писать урл фида и поста на которых прервал парсинг. А в script2.php забирать их из $_GET. Так? Или есть какие-то устоявшиеся решения? 4. Может ли скрипт вызывать сам себя? Скажем, поставить в script.php счетчик времени и когда он отсчитает 10 секунд, запускать себя же? 5. Надо ли вводить какую-нибудь паузу перед вызовом себя же, скажем несколько секунд с помощью sleep ()? Или можно фигачить эту работу нон-стопом? (Если это важно, у меня VPS). 6. Параллельно с процессом парсинга на морду сайта ходят пользователи, кликают по ссылочкам, поднимают из базы сохраненные там посты. Не мешают ли эти процессы друг другу? Я не очень разбираюсь в "однопоточный/многопоточный". Может ли фактически фоновая работа скрипта парсера завешивать работу веб-сайта? Если да, то как эти процессы надо разводить? Спасибо!
Рекомендую присмотреться к PCNTL, т.к. сам с его помощью реализовал ботов, которые круглосуточно лазили по тысячам сайтов. Форк - это независимая копия процесса. Т.е. ты запускаешь скрипт через браузер - он живет, пока его не прибьет сервак, но форк скрипта будет выполняться со всеми теми же параметрами бесконечно долго (time_limit в PHP) начиная со строки, в которой произведен форк. Единственное различие, между материнским процессом и форками - материнскому сразу известен ID форка. Таким образом, ты можешь с помощью простого условия убить материнский и оставить форк в долгом цикле в фоне. Кроме того, ты можешь открыть множество форков, организовав таким образом многопоточность. И на последок, материнский процесс может сохранять идентификаторы форков (наприрмер в файле), и ты можешь сделать управляющий скрипт, чтобы проверять, живы ли форки или прибивать в случае необходимости.
А какой хостинг? Я долгие скрипты обычно запускаю из командной строки Код (Text): php -f script.php Только неясно, какая у вас задача
если речь про скрипт, выполняющийся через вебсервер и вызванный пользователем с его браузера, то можно посоветовать <meta refresh> в шапке страницы. формируешь там ссылку на следующий этап и он будет вызван через заданное количество секунд (как вариант, через 0 секунд). браузер автоматически будет вызывать новый запрос. пользователь способен прервать эту карусель просто закрыв страничку. Код (PHP): echo '<meta http-equiv="refresh" content="5;url='.$urlWithNewParam.'" />'; в самом скрипте не надо пауз. такая пауза будет входить во время выполнения скрипта, с чем боролись, на то и напоролись. в мета рефреш цифра это пауза МЕЖДУ запросами. зависит от твоего скрипта. если кликанье по ссылочкам не изменяет состояние данных, то вероятно НЕ мешают.
Неплохое расширение, но для большинства задач избыточно. Опять же компилить пых с PCNTL придётся. Если задача просто запустить несколько копий скрипта, которые будут отрабатывать пока не будет решена задача, при этом количество работающих копий должно регулироваться, можно решить к примеру так: Код (Text): $delta = time() - (int)filemtime(md5(__FILE__)); if($delta<60 && $delta>0) die('ischild'); set_time_limit(60); ignore_user_abort(1); session_write_close(); // doing great work here if($job_undone)){ touch(md5(__FILE__)); $ch = curl_init(); curl_setopt($ch, CURLOPT_URL, "request_script_here"); curl_setopt($ch, CURLOPT_HEADER, 0); curl_setopt($ch, CURLOPT_TIMEOUT, 2); curl_exec($ch); curl_close($ch); } Использовал для задачи сбора картинок с тысячи внешних серверов и обошёлся без дополнительных либ.
Ааа, у самого сейчас такой же скрипт работает. Я обычно пользуюсь одним из двух велосипедов: 1. Как порекомендовал artoodetoo выше, скрипт делает, что надо, потом ставит хедер и сам на себя редиректится. 2. Скрипт вызывает через Аякс другой скрипт, который выполняет нужные действия, говорит "Все ок", по возврату ответа от второго скрипта первый снова вызывает ajax.
Хостинг - Русоникс, VPS, 1Гб ОЗУ. Купил с опцией Русоникса "Сервер без хлопот", когда админы компании отвечают за поддержку сервера, как минус - насколько я понимаю, у меня нет "рут-доступа" (не уверен - надо ли еще что-то указать?) Ну, ок, опишу без абстракций. Я веду базу данных rss/atom фидов СМИ. Парсю их заметки в свою БД. И формирую предустановленные сложные поисковые запросы к этому контенту, которые дают довольно чистую выдачу по специфическим темам (если просто искать в поисковиках будет много мусора). Кроме того, я присваиваю заметкам в базе веса в зависимости от их "социального веса" - количества ретвитов и шар/лайков в Facebook. Это позволяет сортировать выдачу и по хронологии и по популярности. У меня есть два длинных процесса: 1.обход фидов и парсинг заметок в базу 2. обновление количества ретвитов по API Twitter. Например, способ, которым я сейчас получаю ретвиты "стоит" 0,1 - 0,3 сек на 1 заметку. Итого, 10 000 заметок - около 1 часа. То же и с парсингом. В каждом фиде от 10 до 25 заметок, фидов несколько сотен (в перспективе, видимо, несколько тысяч). Итого, у меня есть "веб-процессы" на фронтенде: пользователь зашел на морду, кликнул на ссылку нужной тематической выборки и получил выдачу (ну или сформировал свою, потом кликнул). Тут все просто. Обычная история со СМИ или блогом на php. А есть фоновая задача - парсинг XML-фидов и апдейт ретвитов. Вот пытаюсь понять, как в принципе оптимально эту задачу решить. (php изучаю с начала года, свободно ориентируюсь на уровне "учебника" по работе с php и MySQL. Но вот вещи типа PCNTL, "запусти из консоли" и "напиши демона" ставят меня в тупик - это зияющие дыры в моих знаниях)) Впрочем, как мне кажется, я быстро учусь, главное понять в какую сторону рыть.) Спасибо! Добавлено спустя 8 минут 47 секунд: Огромное спасибо за сценарий! Я выше в комментарии в ответ на совет описать подробнее задачу, изложил, что делает скрипт (не буду тут копипастить, чтобы не засорять ветку), посмотрите плиз, если не сложно. Собственно, мои "долгие процессы", вызваны не пользователем из веба. Это я парсю кучу XML фидов и собираю для полученных заметок количество ретвитов через АПИ Твиттера. Это фоновые процессы, которые наполняют базу сайта. А пользователь кликает по ссылочкам на сайте, получая выдачу из моей БД, как в обычном СМИ. (ну разве что выдача специальным образом сформирована) И видимо в этом случае мне этот путь не очень подходит. Потому что если считать этим "пользователем" меня, то рано или поздно я страничку, с которой вызвал скрипт, закрою и его работа прекратится, так? Прошу прощения, что не сообразил сразу описать все в подробностях (думал, это лишнее). Буду признателен, если найдете минутку прокомментировать с учетом "новых данных" )) Спасибо! Добавлено спустя 3 минуты 15 секунд: Спасибо! Осмыслю и попробую! (пока тут для меня много неизвестных вещей)) пошел читать)) )
консоль = командная строка, и в твоей ситуации это будет оптимальное решение. Скрипт, который запущен в консоли, не связан с веб-сервером. На VPS ты имеешь возможность использовать crontab - список команд (заданий), выполняемых автоматически по расписанию. Например, запись Код (Text): */15 * * * * php /путь/к/скрипту.php приведет к запуску скрипта каждые 15 минут. И для этого не придется открывать страницу в браузере.
А! Крон! Понял, спасибо. Правильно я понял, что на скрипты запущенные из крона "тайм-аут" сервера не действует? Но ведь не может же скрипт работать вечно, я читал, что сервер периодически перезагружается... Есть ли для таких задач рекомендованное время, которым стоит ограничивать выполнение таких скриптов? Или какие-то привязки (посмотреть настройку Х и сделать время Y), или как-то в скрипте обыгрывать то, что возможно сервер его все-таки прервет из вне? Спасибо!
Ему вероятнее всего нужно запускать скрипт в несколько потоков, а не просто каждые 15 минут. Также нужно регулировать количество потоков. Скрипт может отрабатывать разное время (внутреннюю организацию не знаем, но, учитывая что идёт работа с внешними ресурсами — почти всегда они отрабатывают за разное время, которое обычно не нужно жёстко урезать, ограничивая лишь число итераций), если просто плодить их без оглядки каждые 15 минут то ни к чему хорошему это не приведёт. bocharsky, я вам представил готовое решение вашей задачи. Где нужно сделать ctrl+C ctrl+V и почитать что значат все вызываемые команды, чтобы понять что делает сценарий. Ставьте его в cron с вызовом каждый час и он будет запускать себя в несколько потоков пока не выполнит задачу. Перезагрузки сервера и тому подобные вещи никак неповлияют на его работу. Он запустится по расписанию и наплодит себе потоки для быстрого решения задачи.
Да. Но действует директива max_execution_time из php.ini. Установить свой лимит можно прямо в скрипте, с помощью set_time_limit. Сервер в смысле машина не должен перезагружаться без явной причины. Обычно сервера работают неделями без перезагрузок, так что php-скрипт без ограничения времени (set_time_limit(0)) может работать очень долго. Чтобы минимизировать риски, связанные с прерыванием выполнения скрипта, нужно объединять sql-запросы в транзакции и не сохранять промежуточных результатов. Добавлено спустя 4 минуты 57 секунд: естественно. Крон это только оптимальный способ запуска. Во-первых, нужно выставить лимит, чтобы избежать безнадежных зависаний, и использовать транзакции, чтобы не потерять данные, если скрипт завершится по тайм-ауту. Во-вторых, можно применить один из способов контроля исполнения которые были описаны в теме.
нет неправильно. таймаут есть, но он может быть задан отдельно от "веб-версии". если у тебя VDS, ты можешь сам редактировать эти параметры. на моем Debian конфигурация PHP располагается так: Код (Text): /etc /php5 /cli php.ini - конфиг для php, запускаемого из командной строки (из cron) /fpm php.ini - конфиг для php, запускаемого из-под nginx в каждом можно задать свой max_execution_time, по умолчанию он везде 30сек. не стоит его сильно увеличивать, пусть зависшие скрипты таки умирают! надо именно логику скриптов выстраивать так, чтобы они могли обрабатывать данные частями. Добавлено спустя 5 минут: p.s. cron запускается с точностью до минуты. допустим ты НЕ меняешь max_execution_time, а сам добиваешся, что скрипт работает меньше 30сек. тогда можно запускать скрипт по крону хоть каждую минуту. важно чтобы несколько экземпляров не боролись за ресурсы ))) если ты увелишь время до 5мин, скажем. то надо будет придумать какой-то флажек, лочить файл, например. чтобы второй экземпляр не наступил на пятки первому.
надо разобраться в терминологии ) Сервер как вся система, или только программа-веб сервер. Я исходил из второго, т.к. время обработки HTTP запроса обычно ограничено, и этот лимит приводит к завершению скрипта по причине потери соединения с клиентом.
Спасибо! Уже читаю! )) Но, сами понимаете, задать уточняющие вопросы по всем стратегиям и советам и понять их получше - дело не лишнее! Над этим тоже думаю. Может я сейчас неграмотно выскажусь, но пока исхожу из такой логики: обновлять количество ретвитов у "свежих заметок" (в первые несколько часов / сутки) надо чаще, чем у "старой заметки", которой уже, например, несколько месяцев, или год. Итого, "свежие заметки" (полученные сегодня) надо обходить, скажем, раз в час, а старые - можно и раз в месяц. Если я правильно понимаю, это удобно сделать с помощью двух разных скриптов, которые будут работать параллельно. Та же логика, видимо распространяется и на парсинг фидов: есть те, где заметки появляются раз в несколько минут, итого rss из 20 заметок полностью обновляется где-то в течении часа, есть фиды, где заметки появляются раз в неделю (персональные блоги), туда вообще можно раз в месяц ходить. Наверное, эту работу тоже можно разнести по разным скриптам. Ну и как минимум, а) парсинг фидов и б) сбор ретвитов в принципе просятся в разные скрипты, т.е. в разные процессы. (если говорю ерунду, или путаю теплое с мягким - поправьте плиз) И конечно всем огромное спасибо за советы! Читаю, думаю, пробую )))
Об исполнении и отдаче результатов клиенту речи нет у тс. Ваше дело. Нам тут структура и цели вашего приложения всецело не известны. Процесс у вас один, потоков будет несколько если код будет выполнятся в несколько потоков.
"Пришел Сурикат и все испортил" Автор, есть вероятность, что вы пытаетесь улететь в космос с помощью катапульты. Вы делаете на PHP работу, которую, по-хорошему, он делать не должен. Если у вас такие объемы работы, то заведите параллельно пыху какую-нибудь софтину на плюсах, или, на худой конец, шарпах/яве. И пусть она жует эти XML-ки и складывает их в базу. А PHP пусть занимается тем, чем должен - формировать динамические страницы по запросу клиента. На сурьезных проектах поступают именно так. Просто потому что каждый инструмент нужен для своего дела. Закручивать гайку отверткой только потому, что гаченым ключом не умеете пользоваться, увы, путь вникуда. Не будьте тем мужиком ихз анекдота, который ночью потерял ключи в переулке, а искать их пошел под ближайший фонарный столб, аргументируя тем, что там искать легче, потому что светлее.
Сурикат, оно конечно можно и написать на более подходящем языке, можно даже привлечь под это дело стороннего специалиста, но встает вопрос поддержки одной конкретной тулзы на очень подходящем языке. К тому же пых уже давно легко жует большие xml-ки, быстро и не расходуя память. xml stream reader в помощь. Серьезно, пых уже давно перестал быть продвинутым шаблонизатором, просто многие ещё застряли в тех временах и упорно не хотят видеть изменений. Конечно, есть реально тяжелые задачи, которые лучше переложить на другие, более подходящие для этого языки. Но тут явно не тот случай, имхо. Разбиваешь все это на атомарные задачи, заводишь в бд лог обновлений из которого можно будет в любой момент решить что пора обновлять, что добавилось, а что может и подождать, запускаешь сервер очередей и забиваешь его тасками, наращивая количество воркеров до приемлемой загруженности проца / оперативки. Проблема решена )