Запускать задачу PHP асинхронно

def flatten(arr):
    if not isinstance(arr, list):
        return arr
    else:
        output = []
        for sy in arr:
            if isinstance(sy, list):
                temp = flatten(sy)
                for py in temp:
                    output.append(py)
            else:
                output.append(sy)
        return output

print (flatten([1,2,3,(4,5,6),[7,8,9]]))
#[1, 2, 3, (4, 5, 6), 7, 8, 9]
136
задан davr 13 May 2009 в 16:12
поделиться

7 ответов

Я использовал метод организации очередей, и он работает хорошо, так как вы можете отложить эту обработку до тех пор, пока нагрузка на сервер не перестанет работать, что позволяет вам довольно эффективно управлять нагрузкой, если вы можете разделить «задачи, не срочно "легко.

Свернуть свой собственный не слишком сложно, вот еще несколько вариантов, которые стоит проверить:

  • GearMan - этот ответ был написан в 2009 году, и с тех пор GearMan выглядит популярным вариант, см. комментарии ниже.
  • ActiveMQ , если вам нужна полноценная очередь сообщений с открытым исходным кодом.
  • ZeroMQ - это довольно классная библиотека сокетов, которая позволяет легко писать распределенный код, не беспокоясь о самом программировании сокетов.
76
ответ дан 23 November 2019 в 23:41
поделиться

PHP является однопоточным языком, поэтому не существует официального способа запустить с ним асинхронный процесс, кроме использования exec или popen . Об этом есть запись в блоге здесь . Ваша идея очереди в MySQL также является хорошей идеей.

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

-4
ответ дан 23 November 2019 в 23:41
поделиться

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

Если вы будете искать в сети материал для потоковой передачи PHP, некоторые люди придумали способы имитации потоков на PHP.

1
ответ дан 23 November 2019 в 23:41
поделиться

Это тот же метод, который я использую уже несколько лет и не знаю не видел и не нашел ничего лучше. Как говорили люди, PHP является однопоточным, так что вы больше ничего не можете сделать.

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

Я также использовал аналогичный процесс для создания отчетов. Я не уверен, что буду использовать "фоновую обработку" для чего-то вроде электронной почты, если нет реальной проблемы с медленным SMTP. Вместо этого я мог бы использовать таблицу в качестве очереди, а затем иметь процесс, который запускается каждую минуту для отправки электронных писем в очереди. Вам нужно будет опасаться отправки электронных писем дважды или других подобных проблем. Я бы рассмотрел аналогичный процесс организации очереди и для других задач.

4
ответ дан 23 November 2019 в 23:41
поделиться

Here is a simple class I coded for my web application. It allows for forking PHP scripts and other scripts. Works on UNIX and Windows.

class BackgroundProcess {
    static function open($exec, $cwd = null) {
        if (!is_string($cwd)) {
            $cwd = @getcwd();
        }

        @chdir($cwd);

        if (strtoupper(substr(PHP_OS, 0, 3)) == 'WIN') {
            $WshShell = new COM("WScript.Shell");
            $WshShell->CurrentDirectory = str_replace('/', '\\', $cwd);
            $WshShell->Run($exec, 0, false);
        } else {
            exec($exec . " > /dev/null 2>&1 &");
        }
    }

    static function fork($phpScript, $phpExec = null) {
        $cwd = dirname($phpScript);

        @putenv("PHP_FORCECLI=true");

        if (!is_string($phpExec) || !file_exists($phpExec)) {
            if (strtoupper(substr(PHP_OS, 0, 3)) == 'WIN') {
                $phpExec = str_replace('/', '\\', dirname(ini_get('extension_dir'))) . '\php.exe';

                if (@file_exists($phpExec)) {
                    BackgroundProcess::open(escapeshellarg($phpExec) . " " . escapeshellarg($phpScript), $cwd);
                }
            } else {
                $phpExec = exec("which php-cli");

                if ($phpExec[0] != '/') {
                    $phpExec = exec("which php");
                }

                if ($phpExec[0] == '/') {
                    BackgroundProcess::open(escapeshellarg($phpExec) . " " . escapeshellarg($phpScript), $cwd);
                }
            }
        } else {
            if (strtoupper(substr(PHP_OS, 0, 3)) == 'WIN') {
                $phpExec = str_replace('/', '\\', $phpExec);
            }

            BackgroundProcess::open(escapeshellarg($phpExec) . " " . escapeshellarg($phpScript), $cwd);
        }
    }
}
5
ответ дан 23 November 2019 в 23:41
поделиться

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

Я сделал с ним несколько вещей:

  • Изменение размера изображения - и при передаче слегка загруженной очереди на PHP-скрипт на основе интерфейса командной строки изменение размера больших (2 МБ +) изображений работало нормально, но попытка изменение размера одних и тех же изображений в экземпляре mod_php регулярно вызывало проблемы с пространством памяти (я ограничил процесс PHP до 32 МБ, и изменение размера потребовало большего)
  • проверки в ближайшем будущем - beanstalkd имеет доступные задержки (сделайте это задание доступно для запуска только через X секунд) - так что я могу запустить 5 или 10 проверок на событие, немного позже

Я написал систему на основе Zend-Framework для декодирования «красивого» URL-адреса, например , чтобы изменить размер изображения, он вызовет QueueTask ('/ image / resize / filename / example.jpg') . URL-адрес сначала был декодирован в массив (модуль, контроллер, действие, параметры), а затем преобразован в JSON для внедрения в саму очередь.

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

Я также добавил одну морщину, что cli-скрипт выполнялся только для 50 циклов перед перезапуском, но если он действительно захочет перезапустить, как планировалось, он сделает это немедленно (запускается через bash-скрипт). Если возникла проблема, и я сделал exit (0) (значение по умолчанию для exit; или die (); ), он сначала остановился бы на пару секунд.

/image/resize/filename/example.jpg'). URL-адрес сначала был декодирован в массив (модуль, контроллер, действие, параметры), а затем преобразован в JSON для внедрения в саму очередь.

Затем длительный скрипт cli взял задание из очереди, запустил его ( via Zend_Router_Simple), и, если необходимо, поместите информацию в memcached, чтобы PHP веб-сайта мог подхватить ее по мере необходимости, когда это будет сделано.

Я также добавил одну морщину, что cli-скрипт выполнялся только для 50 циклов перед перезапуском, но если он действительно захочет перезапустить, как планировалось, он сделает это немедленно (запускается через bash-скрипт). Если возникла проблема, и я сделал exit (0) (значение по умолчанию для exit; или die (); ), он сначала остановился бы на пару секунд.

/image/resize/filename/example.jpg'). URL-адрес сначала был декодирован в массив (модуль, контроллер, действие, параметры), а затем преобразован в JSON для внедрения в саму очередь.

Затем длительный скрипт cli взял задание из очереди, запустил его ( via Zend_Router_Simple), и, если необходимо, поместите информацию в memcached, чтобы PHP веб-сайта мог подхватить ее по мере необходимости, когда это будет сделано.

Я также добавил одну морщину, что cli-скрипт выполнялся только для 50 циклов перед перезапуском, но если он действительно захочет перезапустить, как планировалось, он сделает это немедленно (запускается через bash-скрипт). Если возникла проблема, и я сделал exit (0) (значение по умолчанию для exit; или die (); ), он сначала остановился бы на пару секунд.

URL-адрес сначала был декодирован в массив (модуль, контроллер, действие, параметры), а затем преобразован в JSON для внедрения в саму очередь.

Затем длительный скрипт cli взял задание из очереди, запустил его ( via Zend_Router_Simple), и, если необходимо, поместите информацию в memcached, чтобы PHP веб-сайта мог подхватить ее по мере необходимости, когда это будет сделано.

Я также добавил одну морщину, что cli-скрипт выполнялся только для 50 циклов перед перезапуском, но если он действительно захочет перезапустить, как планировалось, он сделает это немедленно (запускается через bash-скрипт). Если возникла проблема, и я сделал exit (0) (значение по умолчанию для exit; или die (); ), он сначала остановился бы на пару секунд.

URL-адрес сначала был декодирован в массив (модуль, контроллер, действие, параметры), а затем преобразован в JSON для внедрения в саму очередь.

Затем длительный скрипт cli взял задание из очереди, запустил его ( via Zend_Router_Simple), и, если необходимо, поместите информацию в memcached, чтобы PHP веб-сайта мог подхватить ее по мере необходимости, когда это будет сделано.

Я также добавил одну морщину, что cli-скрипт выполнялся только для 50 циклов перед перезапуском, но если он действительно захочет перезапустить, как планировалось, он сделает это немедленно (запускается через bash-скрипт). Если возникла проблема, и я сделал exit (0) (значение по умолчанию для exit; или die (); ), он сначала остановился бы на пару секунд.

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

Один морщина, которую я также добавил, заключалась в том, что cli-скрипт выполнялся только для 50 циклов перед перезапуском, но если он действительно хотел перезапустить, как планировалось, он делал это немедленно (запускался через bash-скрипт). Если возникла проблема, и я сделал exit (0) (значение по умолчанию для exit; или die (); ), он сначала остановился бы на пару секунд.

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

Один морщина, которую я также добавил, заключалась в том, что cli-скрипт выполнялся только для 50 циклов перед перезапуском, но если он действительно хотел перезапустить, как планировалось, он делал это немедленно (запускался через bash-скрипт). Если возникла проблема, и я сделал exit (0) (значение по умолчанию для exit; или die (); ), он сначала остановился бы на пару секунд.

Еще одна проблема, которую я добавил, заключалась в том, что cli-скрипт выполнялся только в течение 50 циклов перед перезапуском, но если он действительно хотел перезапустить, как планировалось, он делал это немедленно (запускался через bash-скрипт). Если возникла проблема, и я сделал exit (0) (значение по умолчанию для exit; или die (); ), он сначала остановился бы на пару секунд.

Еще одна проблема, которую я добавил, заключалась в том, что cli-скрипт выполнялся только в течение 50 циклов перед перезапуском, но если он действительно хотел перезапустить, как планировалось, он делал это немедленно (запускался через bash-скрипт). Если возникла проблема, и я сделал exit (0) (значение по умолчанию для exit; или die (); ), он сначала остановился бы на пару секунд.

7
ответ дан 23 November 2019 в 23:41
поделиться

Другой способ разветвлять процессы - использовать curl. Вы можете настроить свои внутренние задачи как веб-сервис. Например:

Затем в ваших скриптах, доступных пользователю, вызовите службу:

$service->addTask('t1', $data); // post data to URL via curl

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

Добавление HTTP-авторизации или настраиваемой схемы авторизации (например, веб-службы Amazon) позволяет вам открыть свои задачи для использования другими людьми / services (если хотите), и вы можете пойти дальше и добавить сверху службу мониторинга для отслеживания очереди и статуса задачи.

17
ответ дан 23 November 2019 в 23:41
поделиться
Другие вопросы по тегам:

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