Масштабируемая, задержанная обработка PHP

Я работаю над приложением PHP онлайн, которое имеет потребность в отложенном событии PHP. В основном я должен смочь выполнить произвольный код PHP x много секунд (но это могли быть дни) после начального хита к URL. Мне нужно довольно точное выполнение их событие PHP, также я хочу, чтобы оно было довольно масштабируемо. Я стараюсь избегать потребности запланировать задание крона для выполнения каждую секунду. Я изучал Gearman, но это, кажется, не обеспечивает способности запланировать события и как я понимаю, PHP действительно не предназначен для выполнения как демон.

Это было бы идеально, если я мог бы сказать некоторому внешнему процессу опрашивать "URL" средства проверки события на сервере PHP в точное время, когда следующее событие должно быть выполнено. Это время опроса должно будет смочь к уменьшенному или увеличенному по желанию, так как событие может быть удалено и добавлено к очереди и. Какие-либо идеи об изящном способе выполнить это? Существует просто ко много служебному в вызове PHP внешне (имеющий необходимость проанализировать Запрос HTTP или звонящий через CLI) для создания этой идеи выполнимой для моих потребностей.

Мой текущий план является записью демон PHP, который выполнит событие и интерфейс с ним с сервера PHP с gearman. Демон PHP был бы сборкой вокруг SplMinHeap так, надо надеяться, производительность не будет к плохо. Эта идея оставляет дурной тон в моем рту, и я задавался вопросом, была ли у кого-либо лучшая идея? Идеи изменяются немного. Считайте Редактирование 2.

Править:

Я создаю онлайн-игру, которая развивает плееры, сменяющиеся с переменным ограничением по времени. Я использую XMPP и ЕРУНДУ, чтобы позволить мне продвигать сообщения к и от моих клиентов, но у меня есть та часть все сделанные и работа. Теперь я пытаюсь добавить случайное событие, которое инициировало после игры от клиента для разрешения клиенту (и другие люди в игре), что он брал к долго. Я не могу использовать синхронизированный, включают сторону клиента, потому что это было бы годно для использования (так как клиент может играть собой). Надежда, которая помогает.

РЕДАКТИРОВАНИЕ 2:

Спасибо всем за Вашу обратную связь. В то время как я думаю, что большинство Ваших идей работало бы хорошо над мелким масштабом, у меня есть чувство, что они не масштабировались бы очень хорошо (менеджер внешнего события) или испытали бы недостаток в точности, которой этот проект требует (КРОН). Кроме того, в обоих из тех случаев они - внешние части, которые могли привести к сбою и добавить сложность к уже сложной системе.

Я лично чувствую, что единственное чистое решение, которое отвечает требованиям для этого проекта, состоит в том, чтобы записать демону PHP, который обрабатывает отложенные события. Я начал писать то, что я думаю, первый PHP runloop. Это обрабатывает наблюдение сокетов и выполнение отложенных событий PHP. Надо надеяться, когда я ближе к тому, чтобы быть сделанным с этим проектом, я могу развесить источник, если какой-либо из Вас интересуется им. До сих пор в тестировании его показал для обещания решения (никакие проблемы с утечкой памяти или нестабильностью).

РЕДАКТИРОВАНИЕ 3: Вот ссылка на библиотеку цикла событий PHP под названием LooPHP для тех, кому интересно.

TL; требования DR

  • Назовите (предпочтительно исходно) PHP во время задержки (в пределах от секунд ко дням)
  • Создание/обновление/удаление дескриптора событий произвольно (я ожидаю большое количество отмененного вызова).
  • Дескриптор высокая загрузка запланированных событий (100-1000 в секунду на сервер)
  • Вызовы должны быть в течение одной секунды после, он запланировал время
  • В этой точке я не открыт для перезаписи кодовой базы на другой язык (возможно, однажды, я буду),

19
задан Kendall Hopkins 16 July 2010 в 19:54
поделиться

12 ответов

Я думаю, что решение только для PHP будет трудно (почти невозможно) реализовать. Я придумал два решения вашей проблемы.

Решение на PHP/Redis

Вопрос задал Kendall:

  • Насколько стабилен redis:

Redis очень стабилен. Разработчик действительно пишет чистый код на C. Вы должны проверить его на github ;). Также многие крупные сайты используют Redis. Например, github. У них был очень интересный блог пост о том, как они сделали github быстрым :). Также superfeedr использует redis. Есть еще много крупных компаний, которые используют redis ;). Я бы посоветовал вам погуглить на эту тему ;).

  • Насколько redis дружелюбен к PHP:

PHP очень дружелюбен к PHP. Многие пользователи пишут PHP-библиотеки для redis. Протокол действительно прост. Вы можете отлаживать его с помощью telnet ;). Если быстро посмотреть на predis, например, там реализована блокировка pop.

  • как мне удалять события:

Я думаю, вам нужно использовать что-то вроде ZRemCommand.

Redis - это продвинутое хранилище ключевых значений. Оно похоже на memcached, но набор данных не является волатильным, а значения могут быть строками, точно как в memcached, но также списками, наборами и упорядоченные множества. Все эти типы данных могут манипулировать с помощью атомарных операций для вставки/выталкивания элементов, добавления/удаления элементов, выполнять объединение на стороне сервера, пересечение, различие между множествами, и так далее. Redis поддерживает различные возможности сортировки.

Что я придумал (Pseudo-code....):

processor.php:

<?php
######----processer.php
######You should do something like nohup php processor.php enough times for processors to run event. 
#$key: should be unique, but should also be used by wakeup.php
while(true) {
    $event = blpop($key); #One of the available blocking threads will wakeup and process event
    process($event); #You should write process. This could take some time so this process could not be available
    zrem($key1, $event); #Remove event after processing it. Added this later!!!!!!
}

client.php:

######----client.php
######The user/browser I guess should generate these events.
#$key1: should be unique.
#$millis: when event should run
#$event: just the event to work on.

if ("add event") {
  zadd($key1, $millis, $event);
} else if ("delete event") {
  zremove($key1, $event)
}

#Get event which has to be scheduled first
$first = zrange($key1, 0, 0);

if ($oldfirst <> $first) { #got different first event => notify wakeup.php.
    lpush($key2, $first);
}

$oldfirst = $first;

wakeup.php:

####wakeup.php
#### 1 time do something like nohup php wakeup.php
#http://code.google.com/p/redis/wiki/IntroductionToRedisDataTypes => read sorted set part.
while(true) {
    $first = zrange($key1, 0, 0);
    $event = blpop($key2, $timeoutTillFirstEvent);

    if ($event == nill) {
        #Blockingqueue has timedout which means event should be run by 1 of blocking threads.
        blpop($key2, $first);
    }    
}

Что-то в этом роде, вы также можете написать довольно эффективный планировщик, используя PHP (Окей, Redis - это C, так что kickass fast :)) только и это будет довольно эффективно :). Я бы тоже хотел написать такое решение, так что следите за новостями ;). Я думаю, что смогу написать пригодный для использования прототип за день....

Мое java-решение

Сегодня утром я, кажется, создал java-программу, которую вы можете использовать для своей проблемы.

  1. download:

    Посетите страницу загрузки на github, чтобы скачать jar-файл (со всеми включенными зависимостями).

  2. install:

    java -jar schedule-broadcaster-1.0-SNAPSHOT-jar-with-dependencies-1277709762.jar

  3. Запустите простые PHP сниппеты

    1. Первый php -f scheduler.php
    2. Следующий php -f receiver.php
  4. Вопросы

    Я создал эти небольшие сниппеты, чтобы, надеюсь, вы поняли, как использовать мою программу. Также есть немного документации в WIKI.

TaskQueue App Engine

Быстрое решение - использовать очередь задач Google App Engine, которая имеет разумную бесплатную квоту. После этого вам придется платить за то, что вы используете.

Используя эту модель, App Engine's Task API очереди позволяет вам указывать задачи как HTTP-запросы (как содержимое запроса как его данные, и целевой URL запроса как его код ссылка). Программно ссылаться на объединенный HTTP-запрос таким образом иногда называют "веб хук".

Важно отметить, что автономная природа API Task Queue позволяет вам указывать веб-крючки заранее, не не дожидаясь их фактического выполнения. Таким образом, приложение может создать множество веб-хуков одновременно и затем передать их App Engine; система будет затем обрабатывать их асинхронно в фоновом режиме (путем "вызова" HTTP запрос). Эта модель веб-хуков позволяет эффективную параллельную обработку - App Engine может вызывать несколько задач, или веб-крючков одновременно.

Подводя итог, можно сказать, что Task Queue API позволяет разработчику выполнять работу в в фоновом режиме, асинхронно, путем разбивая эту работу на автономные веб крючки. Система будет вызывать эти веб-крючки от имени приложения, планируя оптимальную производительность путем возможно, выполняя несколько веб-крючков параллельно. Эта модель гранулированных единиц работы, основанная на стандарте HTTP стандарте, позволяет App Engine эффективно выполнять фоновую обработку таким образом, что она работает с любым языком программирования или фреймворком для веб-приложений.

7
ответ дан 30 November 2019 в 05:06
поделиться

Используйте функцию sleep: http://php.net/sleep

-9
ответ дан 30 November 2019 в 05:06
поделиться

Я бы просто использовал cron для запуска PHP-файла каждые 5 минут. PHP-файл будет проверять, есть ли события, которые должны быть запущены в течение следующего интервала, получать список событий интервала и спать до следующего события. Просыпаемся, запускаем следующее событие (события) из списка, спим до следующего, повторяем, пока не будет сделано.

Вы даже можете масштабировать его, создав или запустив другой php-файл для фактического запуска события. Тогда вы сможете одновременно вызывать более одного события.

0
ответ дан 30 November 2019 в 05:06
поделиться

Я не уверен, почему вы пытаетесь избежать использования cron. Вы можете создать очередь запросов в таблице и заставить cron запускать процесс для проверки текущих заданий.

Есть несколько проблем, в зависимости от ваших конкретных требований. Например:

  • Насколько точным должен быть вызов?
  • Сколько времени займет каждый вызов?
  • Какова нормальная и пиковая нагрузка в любой заданный период?

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

У меня много демонов, которые запускают PHP (используя daemontools). При таком подходе вы можете удерживать запросы в ядре и выполнять внутренние операции в любое время.

Однако, если вам нужен точный и надежный расчет времени, вам, вероятно, следует вообще отказаться от PHP.

0
ответ дан 30 November 2019 в 05:06
поделиться

Что насчет этого:

http://www.phpjobscheduler.co.uk/

0
ответ дан 30 November 2019 в 05:06
поделиться

Я также рекомендую стратегию очереди, но вам, похоже, не нравится использование базы данных в качестве очереди. У вас есть инфраструктура XMPP, поэтому используйте ее: используйте узел pubsub и публикуйте свои события на этом узле. Pubsub можно дополнительно настроить для постоянного хранения невыгруженных элементов.

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

2
ответ дан 30 November 2019 в 05:06
поделиться

Кажется, это идеальное место для Очереди событий в базе данных.

Пусть ваши пользовательские события (запускаемые при посещении веб-страницы) создают запись в базе данных, которая включает инструкции по выполнению действия и отметку времени, когда оно должно произойти. You Daemon (либо постоянное приложение, либо запускаемое CRON) проверяет базу данных на наличие событий, которые должны были произойти ( $ TriggerTime <= time () ) и которые еще не были отмечены как «обработанные». Если вы обнаружите одно или несколько из этих событий, выполните инструкцию и, наконец, отметьте событие как «обработанное» в базе данных или просто удалите запись.

Преимущество использования БД для хранения событий (а не чего-то, что находится в ОЗУ приложения) заключается в том, что вы можете восстановиться после сбоя без потери данных, вы можете иметь более одного чтения за один рабочий день. событие за раз, и вы можете просто изменить событие.

Кроме того, есть много людей, которые используют PHP в качестве общего языка сценариев демонов на серверах и т. Д. Cron может выполнить сценарий PHP (и подтвердить, что экземпляр этого «приложения» уже запущен), который проверяет очередь событий. время от времени. У вас может быть небольшое приложение, которое умирает через минуту бездействия, а затем перезапускается CRON. Приложение может проверять БД на наличие записей с быстрой частотой по вашему выбору (например, 1 с). Обычно Cron не может выполнять временное событие быстрее, чем один раз в минуту.

4
ответ дан 30 November 2019 в 05:06
поделиться

Я не могу придумать ничего, что делало бы все, что вы просили:

  • должно быть очень точным
  • задержка на длительные периоды времени
  • возможность удалить/изменить время события

Тривиальным способом было бы использовать комбинацию следующих функций:

set_time_limit(0);
ignore_user_abort(true);
time_sleep_until(strtotime('next Friday'));
// execute code

Однако, как сказал @deceze, это, вероятно, не очень хорошая идея, так как если вы установите большую задержку, Apache может в конечном итоге убить дочерний процесс (если вы не используете PHP CLI, это бы упростило задачу). Это также не позволит вам изменить/удалить событие, если вы не установите более сложную логику и базу данных для хранения событий. Также, register_shutdown_function() может быть полезен, если вы хотите пойти этим путем.

На мой взгляд, лучшим подходом будет настройка задания CRON.

0
ответ дан 30 November 2019 в 05:06
поделиться

Пусть ваш php-скрипт сделает вызов exec, чтобы запланировать выполнение вашего PHP-скрипта в нужное вам время, используя команду "at"

exec("at 22:56 /usr/bin/php myscript.php");

at выполняет команды в указанное время.

из man-страницы:

At позволяет задавать довольно сложные спецификации времени, расширяя стандарт POSIX.2. стандарт POSIX.2. Он принимает время вида ЧЧ:ММ для запуска задания в определенное время суток. определенное время суток. (Если это время уже прошло, используется следующий день). предполагается следующий день.) Вы также можете указать полночь, полдень или время чая (16:00) и можно указать время суток с суффиксом AM или PM для выполнения утром или вечером. утром или вечером. Вы также можете указать, в какой день будет выполняться задание, указав дату в форме месяц-имя-день с необязательным годом, или указав дату в форме MMDDYY или MM/DD/YY или DD.MM.YY. Указание даты должно следовать за указанием времени суток. Вы также можете указать время типа сейчас + подсчитать единицы времени, где единицы времени могут быть минуты, часы, дни или недели, и вы можете указать at для запуска задания сегодня, добавив к времени суффикс today, и чтобы запустить задание завтра. суффиксом "завтра".

Далее, если вам нужно разрешение времени в одну секунду, пусть ваш скрипт запускается в начале минуты, а затем просто спит n секунд, пока не придет время выполнения.

10
ответ дан 30 November 2019 в 05:06
поделиться

Вот правильный ответ, но он может вам не понравиться.

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

Что вам действительно нужно, так это язык, управляемый событиями, который поддерживает xmpp, и для этого вам не нужно искать дальше node.js / v8 и поддерживающих библиотек XMPP - он изначально поддерживает и разработан именно для того, что вам нужно. вы также можете пойти по пути Java, но если вы хотите быстро портировать и получить целый ряд новых функций и поддержку того, что вы делаете, node - это то, что вам нужно.

Если вы настаиваете на использовании PHP (как я делал это много раз за многие годы), то «самый легкий» и наиболее эффективный способ сделать это - постоянный PHP-демон с Очередью событий в базе данных - к сожалению!

0
ответ дан 30 November 2019 в 05:06
поделиться

Вы можете использовать Node.JS, который является управляемым событиями веб-сервером на основе JavaScript. Запустите его на секретном внутреннем порту со сценарием, который получает уведомление от сценария PHP и затем планирует запуск действия через xx секунд. Действие в Node.JS может быть таким же простым, как запуск сценария PHP на главном веб-сервере.

1
ответ дан 30 November 2019 в 05:06
поделиться

А как насчет использования cron для запуска программы проверки, которая, например, может выполнять данные из БД.

Или использовать команду linux "at" для планирования выполнения какой-либо команды?

0
ответ дан 30 November 2019 в 05:06
поделиться
Другие вопросы по тегам:

Похожие вопросы: