асинхронная обработка с PHP - один воркер на задание

Рассмотрим веб-приложение PHP, целью которого является прием пользовательских запросов на запуск общих асинхронных заданий, а затем создание рабочего процесса / потока для выполнения задания. Задания не особенно интенсивны для ЦП или памяти, но ожидается, что они будут довольно часто блокироваться при вызовах ввода-вывода. В секунду следует запускать не более одного или двух заданий, но из-за длительного времени выполнения может одновременно выполняться много заданий.

Поэтому крайне важно, чтобы задания выполнялись параллельно. Кроме того, каждое задание должно контролироваться демоном-менеджером, ответственным за уничтожение зависших рабочих, прерывание рабочих процессов по запросу пользователя и т. Д.

Как лучше всего реализовать такую ​​систему? Я вижу:

  1. Разветвление работника от менеджера - это вариант самого нижнего уровня, и мне пришлось бы самому внедрять систему мониторинга. Apache - это веб-сервер, поэтому похоже, что этот вариант потребует запуска любых PHP-воркеров через FastCGI.
  2. Используйте какую-то очередь заданий / сообщений. (gearman, beanstalkd, RabbitMQ и т. д.) - Изначально это казалось очевидным выбором. После некоторого исследования я несколько запутался со всеми вариантами. Например, Gearman выглядит так, как будто он разработан для огромных распределенных систем, где есть фиксированный пул рабочих ... поэтому я не знаю, подходит ли он для того, что мне нужно (один работник на задание).

person Josh Johnson    schedule 18.08.2010    source источник


Ответы (3)


Что ж, если вы работаете в Linux, вы можете использовать pcntl_fork раскошелиться на детей. Затем «хозяин» наблюдает за детьми. Каждый ребенок выполняет свою задачу и затем существует нормально.

Лично мне в моих реализациях никогда не требовалась очередь сообщений. Я просто использовал в «мастере» массив с блокировками. Когда ребенок получал работу, он записывал файл блокировки с идентификационным номером задания. Затем хозяин ждал, пока ребенок не выйдет. Если файл блокировки все еще существует после выхода дочернего элемента, то я знаю, что задача не завершена, и повторно запускаю дочерний элемент с тем же заданием (после удаления файла блокировки). В зависимости от вашей ситуации вы можете реализовать очередь в простой таблице базы данных. Вставляйте задания в таблицу и проверяйте таблицу в главном устройстве каждые 30 или 60 секунд на наличие новых заданий. Затем удаляйте их из таблицы только после того, как дочерний элемент закончит (и ребенок удалил файл блокировки). Это могло бы вызвать проблемы, если бы у вас было одновременно запущено более одного «мастера», но вы могли бы реализовать глобальный «мастер-файл pid» для обнаружения и предотвращения нескольких экземпляров ...

И я бы не стал предлагать форк с FastCGI. Это может привести к некоторым очень неясным проблемам, поскольку среда предназначена для сохранения. Вместо этого используйте CGI, если у вас должен быть веб-интерфейс, но в идеале используйте приложение CLI (deamon). Для взаимодействия с мастером из других процессов вы можете либо использовать сокеты для TCP-связи, либо создать файл FIFO для связи.

Что касается обнаружения зависших рабочих, вы можете реализовать систему «биения сердца», при которой дочерний процесс выдает SIG_USR1 главному процессу каждые несколько секунд. Затем, если вы не слышали от ребенка два или три раза, его можно повесить. Но дело в том, что поскольку PHP не является многопоточным, вы не можете определить, завис ли дочерний элемент или он просто ожидает блокирующего ресурса (например, вызова базы данных) ... Что касается реализации «сердцебиения» , вы можете использовать функцию отметки, чтобы автоматизировать работу сердца -beat (но имейте в виду, что блокирующие вызовы по-прежнему не выполняются) ...

person ircmaxell    schedule 18.08.2010
comment
Спасибо. Я делал это несколько раз, и это работает ДЕЙСТВИТЕЛЬНО хорошо. Что ж, я должен сказать, что это работает очень хорошо, если ваши варианты использования соответствуют ограничениям системы (IPC довольно дорого обходится и т. Д.). Если они не очень хорошо выровнены, вам следует использовать настоящую реализацию потоков и язык, отличный от PHP ... - person ircmaxell; 18.08.2010
comment
Однако будьте осторожны с pcntl_fork(). У меня были проблемы с подключениями к базе данных, которые странным образом распределялись между родительским и дочерним процессами. Я не удивлюсь, если некоторые расширения PECL будут иметь похожие причуды. Я бы уклонился от разветвления в PHP и создавал отдельные процессы через exec() и тому подобное, просто чтобы все было проще. - person Frank Farmer; 18.08.2010
comment
Что ж, я явно повторно открываю все соединения в дочернем элементе после разветвления именно по этой причине. Разветвления бояться нечего (пользуюсь довольно часто). Но это много проб и ошибок, поскольку документации по этому поводу не так много. Проблема с выполнением через exec состоит в том, что это значительно усложняет общение и мониторинг (так как для одного exec блокируется, а для двух гораздо сложнее получить идентификатор процесса неблокирующего exec вызова (вызов с &, добавленным к конец))... - person ircmaxell; 18.08.2010
comment
Интересный комментарий о FastCGI. Не знал, но это имеет смысл. В этом конкретном приложении нет причин, по которым главный / менеджер не может быть демоном CLI, поэтому я могу попробовать этот подход. - person Josh Johnson; 18.08.2010

Рабочий пул может быть интересен:

https://github.com/qxsch/WorkerPool

https://github.com/qxsch/WorkerPool/blob/master/examples/asyncExample.php

person JMW    schedule 06.05.2014

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

person Faizal Pribadi    schedule 31.03.2016