Как лучше всего запустить PHP-скрипт в определенное время?

У меня есть сайт, где аукционы заканчиваются в разное время. Мне нужно отправить автоматическое электронное письмо продавцу и покупателю после завершения аукциона, чтобы уведомить их об окончании аукциона и результатах. Очевидно, я не могу дождаться, пока кто-то загрузит страницу, чтобы запустить скрипт, поэтому есть ли хороший способ автоматизировать это, проверяя текущее время и сравнивая его со временем окончания аукциона и запуская этот скрипт?

Сайт находится на сервере UNIX, поэтому задание cron является вариантом, но я обеспокоен тем, что выполнение такого задания cron сильно нагрузит сервер.


person Budove    schedule 28.02.2013    source источник
comment
Могу я спросить, какую серверную инфраструктуру вы используете? Я предполагаю, что вы используете механизм таймера в чем-то другом, кроме PHP? Если вы используете что-то другое, я рекомендую вам прикрепить некоторый код к этому механизму таймера, который также выполняет сценарий отправки почты, когда таймер заканчивается.   -  person L0j1k    schedule 28.02.2013
comment
Я не знаю ни одного таймера, который я мог бы использовать, кроме, возможно, javascript, работающего на странице. Я не хотел использовать javascript, потому что боялся, что кто-то манипулирует им и мошенничает на аукционе.   -  person Budove    schedule 28.02.2013
comment
Что вы используете, чтобы определить, кто выиграет аукцион? Как вы можете определить (с точностью), кто победит? Я был бы очень осторожен с использованием надежного механизма таймера на серверной части (например, в JavaScript в Node.js, Perl или Python... или что-нибудь действительно кроме PHP). Я начинаю понимать, как работает ваш таймер, срабатывает и проверяет файл/базу данных, когда пользователь посещает страницу? Сейчас это может показаться сложным и, возможно, ненужным, но по мере роста вашего приложения вы будете очень рады, что создали основной таймер в фоновом режиме, который запускает все эти механизмы.   -  person L0j1k    schedule 28.02.2013
comment
Да, но страница обновляется каждые 60 секунд. Есть ли у вас какие-либо предложения о том, как это реализовать? Прямо сейчас у меня есть дата и время окончания аукциона, и когда страница загружается, она сверяет это с текущей датой и временем.   -  person Budove    schedule 28.02.2013
comment
Хорошо, но что происходит, когда пользователь просто покидает страницу? Какой механизм обеспечивает правильное завершение аукциона и уведомление нужных пользователей? С надежным бэкендом, таким как JavaScript/Node (или Perl или что-то еще, если вам не нравится Node), вы можете иметь запущенный процесс, выполняющий вычисления и инициирующий то, что необходимо инициировать, а также отправлять уведомления по электронной почте. Кроме того, поскольку оно находится на серверной части, все ваше приложение не будет полностью зависеть от взаимодействия с пользователем для правильной работы. Я бы сказал, что делать это так, как вы сейчас, более опасно.   -  person L0j1k    schedule 28.02.2013
comment
Хотя бы потому, что весь процесс зависит от пользователя (не говоря уже о последствиях безопасности в процессе).   -  person L0j1k    schedule 28.02.2013
comment
Что ж, это моя проблема... попытка уведомить нужного пользователя. Я полагаю, что если пользователь не взаимодействует со страницей, то аукцион не должен заканчиваться, но когда он это делает, вычисляется разница во времени, и пользователь уведомляется об окончании аукциона. Я бы предпочел сделать это с помощью JavaScript, но я не очень хорошо с ним разбираюсь.   -  person Budove    schedule 28.02.2013
comment
Что ж, к счастью, с JavaScript действительно легко разобраться, особенно если вы достаточно знаете PHP, чтобы делать то, что вы уже делаете. Я знаю, это звучит как монументальная задача, но вы будете благодарить меня позже, когда столкнетесь с огромными, огромными проблемами с задачами, когда либо 1) масштабируется ваше приложение, либо 2) ваши пользователи начинают жаловаться, что они не получили уведомления о том, что они потеряли или выиграл, пока они не посетили страницу снова. В конце концов, не все сидят и нажимают «обновить» на eBay.   -  person L0j1k    schedule 28.02.2013
comment
Хорошо, подумав еще немного, я думаю, что таймер JavaScript на странице проверяет дату/время окончания аукциона и начинает обратный отсчет, пока страница открыта. Если он заканчивается, когда страница открыта, то торги закрываются. Кроме того, задание cron, которое запускается каждые 60 секунд. который ищет просроченные аукционы и закрывает их, а также отправляет уведомления по электронной почте. Спасибо за помощь.   -  person Budove    schedule 28.02.2013
comment
Совет: никогда не доверяйте своим клиентам выполнение важной работы. Что бы я сделал в этой ситуации, чтобы быстро решить проблему, так это создал сценарий Perl (поскольку мы просто выбираем тот, который умеем писать), который обращается к базе данных аукционов и содержит пульс ВСЕХ аукционов, то есть он отслеживает системное время и КОНЕЦ всех аукционов. Затем, когда аукцион заканчивается, он подключается к специально написанной локальной веб-странице, которая выполняет всю вашу электронную почту и сортировку в базе данных на PHP. Это решение не самое лучшее, но оно быстрое исправление и перестает полагаться на клиента в важных вещах.   -  person L0j1k    schedule 28.02.2013


Ответы (5)


Задание cron запускается не чаще одного раза в минуту.

Какую бы нагрузку он ни создавал на сервере, действительно зависит от типа сценария, который вы собираетесь запускать. Кстати, я предполагаю, что вы используете cli для запуска скрипта (а не просто выполняете curl http://mysite.com.

Если ваш скрипт занимает больше одной минуты (вы должны следить за этим), просто выполните одно из следующих действий:

  • Увеличьте интервал времени между запусками или,
  • Используйте файл блокировки, чтобы убедиться, что два экземпляра вашего скрипта не могут работать одновременно.

    if (($fp = fopen('/tmp/mylockfile', "r+")) === false) {
        die("Could not open lock file");
    }
    if (!flock($fp, LOCK_EX | LOCK_NB)) {
        die("Could not obtain lock");
    }
    // run your code here
    
    // release the lock and close file
    fclose($fp);
    

OTOH Если сценарий должен запускаться чаще одного раза в минуту, вам понадобится совершенно другой механизм.

person Ja͢ck    schedule 28.02.2013
comment
«вы должны сравнить это», нет! вы должны убедиться, что ваш скрипт останавливается через 59 секунд! - person hek2mgl; 28.02.2013
comment
@hek2mgl Что? Вы понимаете, насколько это непрактично, верно? Если вы, конечно, не шутили. - person Ja͢ck; 28.02.2013
comment
Нет, это не требуется! Я сделал это.. Имея очередь.. Остановка после окончания очереди или через 50 секунд. Если пройдено 50 сек. -› напишите администратору. ТРЕВОГА! . это хорошая работа - person hek2mgl; 28.02.2013
comment
@ hek2mgl Достаточно простого файла блокировки, который гарантирует, что другой экземпляр не сможет работать вместе. - person Ja͢ck; 28.02.2013
comment
«Простой файл блокировки» делает это с помощью PHP (не колоритно). - person hek2mgl; 28.02.2013
comment
@ hek2mgl Я не уверен, что вы имели в виду. - person Ja͢ck; 28.02.2013
comment
не колоритный, значит не уязвимый к условиям гонки, кстати, ваши ответы ok (сейчас). но то же самое относится и к ответу @ L0j1k, за который проголосовали отрицательно. Не знаю, почему минус - person hek2mgl; 28.02.2013
comment
@ hek2mgl Racy означает совсем другое, но использование рекомендательной блокировки является общепринятым способом предотвращения множественных запусков. - person Ja͢ck; 28.02.2013
comment
рекомендательная блокировка — хороший способ справиться с этой проблемой, если вы хотите, чтобы что-то выполнялось только одним процессом. Если вам нужно масштабировать по горизонтали, этого будет недостаточно. - person FoolishSeth; 28.02.2013

В: Как лучше всего запускать PHP-скрипт в определенное время или с определенным интервалом?
О: Используйте хрон

Q: Создает ли cronjob большую нагрузку на сервер?
A: Конечно, зависит от вашего скрипта. Но проверить, должен ли аукцион быть закрыт, закрыть его и отправить два электронных письма, не должно быть сложно. Обязательно создайте какой-нибудь файл блокировки, чтобы убедиться, что если ваш скрипт выполняется дольше установленного интервала, он не запускается дважды.

В: запуск скрипта с интервалом меньше 1 минуты
О: Не могу ответить на этот вопрос. Извините :)

person stUrb    schedule 28.02.2013

Используйте Крон. Он позволяет запускать любую команду не чаще одного раза в минуту: http://clickmojo.com/code/cron-tutorial.html

Что касается нагрузки на сервер, то, как правило, это не будет проблемой, если только вы не выполняете огромное количество вызовов базы данных очень часто на очень низкоуровневом сервере. Я говорю в общих чертах, но идея здравая.

Если вы используете что-то еще (помимо PHP) для запуска механизма таймера аукциона, я рекомендую вам прикрепить некоторый код к этому механизму таймера, который также выполняет сценарий отправки почты, когда таймер доходит до нуля, и определяет победителя.

person L0j1k    schedule 28.02.2013
comment
ОП сказал: задание cron - это вариант, но я обеспокоен тем, что выполнение такого задания cron сильно нагрузит сервер. Если вам интересно, это был не МОЙ отрицательный голос. - person Ja͢ck; 28.02.2013
comment
Это был не мой голос против, но, вероятно, потому, что вы говорите в любое время, когда пожелаете. Это неправда, не более 1 минуты. С аукционом это не удобно. - person stUrb; 28.02.2013
comment
Правильно, поэтому выполните опцию: Используйте cron. Жаль, что ты этого не понял. - person L0j1k; 28.02.2013
comment
Это моя забота. Отправка электронных писем может стать обременительной для сервера, если использование сайта возрастет. Я обеспокоен тем, что на каждое задание cron может приходиться 100 электронных писем... но если скрипт отправляет 1 электронное письмо на скрипт (вместо 100 BCC), будут ли они отправляться одновременно? - person Budove; 28.02.2013
comment
Даже отправка сотен писем в минуту не создаст такой нагрузки, о которой вам следует беспокоиться. Возможно, сотни писем в секунду, но не несколько сотен в минуту. - person L0j1k; 28.02.2013
comment
И тогда, скорее всего, вы столкнетесь с проблемами с маршрутизацией почты (т. е. с срабатыванием флага спама у вашего интернет-провайдера), прежде чем вы столкнетесь с проблемами с сервером, обрабатывающим исходящую почту. - person L0j1k; 28.02.2013
comment
Хорошо, спасибо за помощь. - person Budove; 28.02.2013
comment
Без проблем. Я надеюсь, что это поможет вам. :) - person L0j1k; 28.02.2013

Запустите скрипт PHP как скрипт командной строки. Это не будет нагружать веб-сервер — просто нагрузка на сервер, и вы можете легко запустить его через CRON.

Если вы добавите #!/usr/bin/php в начало скрипта и измените бит выполнения в файле с помощью chmod +x scriptname.php, вы можете напрямую выполнить скрипт, не передавая его через php.

http://php.net/manual/en/features.commandline.php

person Geek Num 88    schedule 28.02.2013
comment
Что ж, с #!/usr/bin/php на самом деле проходит это через PHP, но напрямую интерпретатору. - person L0j1k; 28.02.2013
comment
Извините, я имел в виду запуск через php scriptname.php против scriptname.php - person Geek Num 88; 28.02.2013

Для этого вам нужно сделать несколько вещей:

  1. Сохраните что-нибудь в информации об аукционе, указывающее, отправили ли вы это электронное письмо или нет (может быть логическое значение или дата отправки, которая может быть нулевой). Хотя я должен предположить, что вам нужно сделать что-то помимо отправки этого электронного письма? Пометить аукцион как закрытый, чтобы больше нельзя было делать ставки?

  2. Немного кода, который находит аукционы, которым нужно отправить это электронное письмо: например. они закончились и еще не напомнили.

  3. Что-то, чтобы повторно выполнить часть кода в 2. Вы можете использовать cron. В качестве альтернативы вы можете написать довольно простой демон для unix, который постоянно работает в цикле (подождите хотя бы несколько мс или больше; сделайте что-нибудь). Последнее требует гораздо больше работы, но, на мой взгляд, масштабируется намного лучше. См. http://pear.php.net/package/System_Daemon для некоторых полезных инструментов, если вы повторно заинтересован в этом подходе.

Одна вещь, которую следует учитывать, - это то, насколько вы хотите быть осторожным со случайной двойной отправкой этого электронного письма. Если вы запускаете этот код только в одном потоке, это довольно просто, но если вы когда-нибудь захотите дойти до точки, где у вас есть несколько разных распределенных машин, которые создают и отправляют эти электронные письма, вам нужно быть немного более осторожным. Если вы запускаете его из cron, можете ли вы гарантировать, что один его запуск всегда будет завершен до того, как начнется другой?

person FoolishSeth    schedule 28.02.2013