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

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

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

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

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

1 ответ

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

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

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

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

8
ответ дан 3 November 2019 в 01:52
поделиться
Другие вопросы по тегам:

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