Comments 32
Спасибо, как раз интересовался пдобным, и было дело уже начал писать свое. Но теперь приторможу, и поглубже ознакомлюсь с предоставленным материалом :)
Можно добавить еще starling github.com/starling/starling/tree/master
Не забудьте о tuple space. Не совсем очереди, но тоже полезно.
Еще было интересно, если бы вы в сравнении отметили какие сервера умеют делать persitent queue
это умеет Apache ActiveMQ, Spread и RabbitMQ
Спасибо
Во-первых, совсем непонятно, что имелось в виду под persistent queue.
Если это очередь, которая хранит сообщения до момента их получения потребителем, тогда spread этого не умеет. Во всяком случае, в его оригинальной инкарнации. Есть коммерческий продукт на основе spread, который это умеет. По-моему вот это они: www.spreadconcepts.com/secure_spread_info.html
В spread сделать это вообще проблематично в связи с его основной идеей гарантированности операций с очередями в многосерверной конфигурации. Это чистые in-memory queues.
Если это очередь, которая хранит сообщения до момента их получения потребителем, тогда spread этого не умеет. Во всяком случае, в его оригинальной инкарнации. Есть коммерческий продукт на основе spread, который это умеет. По-моему вот это они: www.spreadconcepts.com/secure_spread_info.html
В spread сделать это вообще проблематично в связи с его основной идеей гарантированности операций с очередями в многосерверной конфигурации. Это чистые in-memory queues.
да помоему они обое имеют поддержку этого. Вот же в описании то одинаково: «Enables message reliability in the presence of machine failures, process crashes and recoveries, and network partitions and merges. ».
Вместе с тем, часть MQ хранит сообщения в базе (или файлы), встроенной или внешней, поэтому это можно считать как раз persistent queue, остальные работают с памятью, однако могут также обеспечить устойчивость к сбоям отдельных узлов (видимо это как раз случай spread), или же заявляют сразу, что не рассчитаны на такие случаи.
Вместе с тем, часть MQ хранит сообщения в базе (или файлы), встроенной или внешней, поэтому это можно считать как раз persistent queue, остальные работают с памятью, однако могут также обеспечить устойчивость к сбоям отдельных узлов (видимо это как раз случай spread), или же заявляют сразу, что не рассчитаны на такие случаи.
Да, в spread именно за счет нескольких серверов обеспечивается reliability.
Если бы они сделали встроенный persistence, цены бы не было системе. Но для упрощения и еще по ряду причин, как я понял, они этого не хотели делать.
Если бы они сделали встроенный persistence, цены бы не было системе. Но для упрощения и еще по ряду причин, как я понял, они этого не хотели делать.
ну а такое есть в MQM хотя по уровню система ниже конечно. И в RabbitMQ подозреваю что есть.
И, конечно, ActiveMQ — activemq.apache.org/persistence.html
И, конечно, ActiveMQ — activemq.apache.org/persistence.html
С другой стороны, гораздо проще, используя spread как network backend, сделать полноценные очереди, чем начинать лепить свои целиком через базу данных, как ниже предлагают, или чем строить сетевую систему с нуля, которая скорее всего будет работать хуже.
Под persistent queue подразумевалась очередь, которая сохраниться при перезапуске сервера очереди.
Т.е. beanstalk не обладает такой очередью, потому что хранить ее в памяти, а starling обладает.
Т.е. beanstalk не обладает такой очередью, потому что хранить ее в памяти, а starling обладает.
Memcacheq — штука хорошая, благо основана на memcachedb, который, в свою очередь, есть смесь memcached и bdb — с производительностью точно проблем не будет. Но есть один момент. Когда разгребаешь на скриптовом сервере очередь php-скриптами, ну, предположим, это обычный цикл while ($item = $queue->getNext()) $this->handle($item), внутри этого самого handle может иногда случиться что-то нехорошее. В корку php упадет из-за порушившейся вдруг шаред мемори акселератора или редкого бага в экстеншене, или просто в датацентре рубильник не тот заденут. А терять данные в очереди совсем не хочется. То есть, если это сбор статистики средней температуры по больнице, то и фиг с ним, а если это биллинг, то как-то не это самое. :)
Я использую очень простое решение. В mysql (или иной РСУБД) заводится табличка (или, если очереди большие, несколько табличек, обрабатываемых параллельно запущенными скриптами) вида
Subscriber_id — это идентификатор «подписчика», то есть тот, кому элемент очереди предназначается. Parameters — произвольные данные, нужные для обработки, например serialize()-ованные.
Обработка очереди выглядит примерно так:
Сам класс выглядит (после небольших упрощений) вот так. Несмотря на кучу завязок на фреймворк, думаю, его логика должна быть понятной.
А что делать с «залипшими» элементами? Очень просто, по крону с разумной регулярностью разгребаем элементы, где status=«LOCKED» и updated достаточно стар, чтобы было ясно, что оно именно залипло — например, как-то так:
А ставить и изучать монстроподобные системы ради такой простой вещи, как очереди, как то мне стремно, хотя надо взглянуть, может, найдутся хорошие идеи, чтобы позаимствовать :)
Я использую очень простое решение. В mysql (или иной РСУБД) заводится табличка (или, если очереди большие, несколько табличек, обрабатываемых параллельно запущенными скриптами) вида
CREATE TABLE `ProcessQueue` ( `id` int(20) NOT NULL AUTO_INCREMENT, `subscriber_id` int(10) NOT NULL DEFAULT '', `error_count` smallint(6) NOT NULL DEFAULT '0', `parameters` blob NOT NULL, `status` enum('QUEUED','LOCKED','FAILED','DELETED') NOT NULL DEFAULT 'QUEUED', `created` timestamp NOT NULL DEFAULT '0000-00-00 00:00:00', `updated` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP, PRIMARY KEY (`id`), KEY `updated` (`updated`,`subscriber_id`,`status`) ) ENGINE=InnoDB DEFAULT CHARSET=utf8
Subscriber_id — это идентификатор «подписчика», то есть тот, кому элемент очереди предназначается. Parameters — произвольные данные, нужные для обработки, например serialize()-ованные.
Обработка очереди выглядит примерно так:
$PQ = new ProcessQueue($subscriber_id); $element = false; $error = false; while ($element = $PQ->getNextElement($element, $error)) { // ... process $element ... if (error occurred) { $error = true; } else { $error = false; } }
Сам класс выглядит (после небольших упрощений) вот так. Несмотря на кучу завязок на фреймворк, думаю, его логика должна быть понятной.
А что делать с «залипшими» элементами? Очень просто, по крону с разумной регулярностью разгребаем элементы, где status=«LOCKED» и updated достаточно стар, чтобы было ясно, что оно именно залипло — например, как-то так:
UPDATE ProcessQueue SET error_count = error_count + 1, STATUS = IF(error_count>5, 'FAILED', 'QUEUED') WHERE updated < $время_минут_15_назад AND STATUS='LOCKED'
А ставить и изучать монстроподобные системы ради такой простой вещи, как очереди, как то мне стремно, хотя надо взглянуть, может, найдутся хорошие идеи, чтобы позаимствовать :)
ApacheMQ — очень мощьное средство! использовал в одном из проектов. Очень понравилась их реализация AJAX доступа к Queue и подобным.
UFO just landed and posted this here
Для хранения очередей в памяти PHP, конечно, подходит плохо, и демон писать смысла особого нет. Но для хранения очереди можно использовать любую СУБД, я выше приводил пример.
А для скриптов, обрабатывающих очереди, нет ничего страшного. Конечно, держать «вечный» цикл невыгодно — даже сам PHP, работая в fastcgi-режиме, имеет ограничение на количество обрабатываемых одним процессом запросов, чтобы не накапливать утечки памяти. Но вполне можно запускать несколько скриптов из крона параллельно, что-то вроде
Стратегии завершения можно использовать различные. Можно просто выгребать очередь, пока она не кончится, и на этом завершаться; можно поставить дополнительное условие по времени или количеству итераций. А чтобы не запустились одинаковые обработчики одновременно (если это, конечно, специально не задумано), прекрасно подходит стандартная юниксовая техника pid-файлов. Как-то так:
Если есть неободимость распараллелить операции уже после инициализации, тоже стандартный юниксовый подход — fork.
А для скриптов, обрабатывающих очереди, нет ничего страшного. Конечно, держать «вечный» цикл невыгодно — даже сам PHP, работая в fastcgi-режиме, имеет ограничение на количество обрабатываемых одним процессом запросов, чтобы не накапливать утечки памяти. Но вполне можно запускать несколько скриптов из крона параллельно, что-то вроде
$ crontab -e * * * * * /path/to/script1.php * * * * * /path/to/script2.php
Стратегии завершения можно использовать различные. Можно просто выгребать очередь, пока она не кончится, и на этом завершаться; можно поставить дополнительное условие по времени или количеству итераций. А чтобы не запустились одинаковые обработчики одновременно (если это, конечно, специально не задумано), прекрасно подходит стандартная юниксовая техника pid-файлов. Как-то так:
class Script extends Application { // ........... protected function checkPid() { $this->pid = posix_getpid(); $pid = (int) @file_get_contents($this->pidfile); if ($pid) { if (is_dir('/proc') && is_dir('/proc/curproc')) { // use procfs $have_pid = is_dir('/proc/'.$pid); } else { // if no procfs, use /bin/ps. warning: BSD-specific implementation $output = array(); exec('/bin/ps -auxwwwp '.$pid, $output); $have_pid = !empty($output[1]); } if ($have_pid) { $this->logScriptEvent(self::EVENT_WARNING, 'already running with pid '.$pid. ', exiting'); exit(1); } } try { file_put_contents($this->pidfile, $this->pid . "\n"); } catch (PhpException $e) { $this->logScriptEvent(self::EVENT_WARNING, 'could not write pidfile: '.$e->getMessage()); } return true; } // ........... function run() { if (!$this->checkPid()) { $this->logScriptEvent(self::EVENT_NOTICE, 'the script ' . get_class($this) . ' is already running'); exit(0); } // .... } }
Если есть неободимость распараллелить операции уже после инициализации, тоже стандартный юниксовый подход — fork.
Ах, да. На винде, конечно, это все работать не будет (разве что под cygwin). Хотя я склонен считать это скорее преимуществом =)
UFO just landed and posted this here
«работало неплохо…но на нагрузках ложилась…»
:)
Собственно, с работой с процессорами в php проблем то и нет. fork есть, exec есть, popen есть, что еще надо? :) дело-то не в этом, а в том, как обрабатывать параллельные соединения.
А тут, чтобы понимать, как эти все винтики крутятся, обязательно к прочтению RU.UNIX.PROG FAQ — Как писать сервера.
:)
Собственно, с работой с процессорами в php проблем то и нет. fork есть, exec есть, popen есть, что еще надо? :) дело-то не в этом, а в том, как обрабатывать параллельные соединения.
А тут, чтобы понимать, как эти все винтики крутятся, обязательно к прочтению RU.UNIX.PROG FAQ — Как писать сервера.
Меня тут в привате пинают по делу:
«Для проверки существования процесса не нужно делать то, что вы нагородили. Достаточно сделать kill(pid, 0);»
И действительно.
Век живи, век учись :)
«Для проверки существования процесса не нужно делать то, что вы нагородили. Достаточно сделать kill(pid, 0);»
И действительно.
srv01 ~$ php -r 'var_dump(posix_kill(posix_getpid(),0));' bool(true) srv01 ~$ php -r 'var_dump(posix_kill(33333,0));' bool(false)
Век живи, век учись :)
Ну из опенсорсных JBoss Messaging & Sun OpenMQ ещё — с каждого вендора по продукту, как это в Java-мире принято) Правда про интеграцию со сторонними языками не подскажу.
Еще небольшой список по теме:
— WebSphere MQ, кстати, имеет SOAP интерфейс и может быть использована в РНР.
— ObjectWeb JORAM joram.objectweb.org
— OpenJMS openjms.sourceforge.net
— MOM4J mom4j.sourceforge.net
— SwiftMQ www.swiftmq.com
Думаю решение с использованием базы данных от symbix очень интересное, т.к. в большинстве случаев этого с головой хватает.
Еще есть совет из опыта работы с MQ: посмотрите на процесс в целом и подумайте, а нужно ли вам использовать MQ или нет? Как раз учитывая тему высокой производительности, то MQ, особенно с сохранением сообщений, дает большую задержку, которую можно компенсировать только большим количеством параллельных процессов обработки.
— WebSphere MQ, кстати, имеет SOAP интерфейс и может быть использована в РНР.
— ObjectWeb JORAM joram.objectweb.org
— OpenJMS openjms.sourceforge.net
— MOM4J mom4j.sourceforge.net
— SwiftMQ www.swiftmq.com
Думаю решение с использованием базы данных от symbix очень интересное, т.к. в большинстве случаев этого с головой хватает.
Еще есть совет из опыта работы с MQ: посмотрите на процесс в целом и подумайте, а нужно ли вам использовать MQ или нет? Как раз учитывая тему высокой производительности, то MQ, особенно с сохранением сообщений, дает большую задержку, которую можно компенсировать только большим количеством параллельных процессов обработки.
спасибо за ссылки, надо будет все пересмотреть. WebSphere yfмеренно не рассматривал, мне показался уж очень корпоративным и громоздким и тянет за собой много IBM-компонентов от инфраструктуры WebSphere
Как раз WebSphere MQ есть самодостаточный сервер и небольшой к тому же.
небольшой?? от 168 до 600 Мб дистрибутив, к тому же коммерческий продукт (доступна только триал версия). Вот здесь: www.ibm.com/developerworks/downloads/ws/wmq/?S_TACT=105AGX28&S_CMP=TRIALS
>RabbitMQ: Есть интерфейс только для Java и C++. С
а так же perl, ruby, python & PHP.
сам являюсь автором модуля PECL PHP pecl.php.net/package/amqp
www.rabbitmq.com/devtools.html
github.com/ruby-amqp/amqp/
pypi.python.org/pypi/amqp
а так же perl, ruby, python & PHP.
сам являюсь автором модуля PECL PHP pecl.php.net/package/amqp
www.rabbitmq.com/devtools.html
github.com/ruby-amqp/amqp/
pypi.python.org/pypi/amqp
Sign up to leave a comment.
Краткий обзор MQ (Messages queue) для применения в проектах на РНР. Часть 1