Неблокирующие функции PHP

У меня есть проект, в котором пользователь загружает изображение через форму, а сервер выполняет некоторые миниатюры. Процесс создания миниатюр очень медленный, поэтому я подумал, что выполнение изменения размера изображения с помощью неблокирующей функции может быть хорошим решением. Я имею в виду: сервер обрабатывает форму (у которой больше полей) дает обратную связь «ok» пользователю, а затем вызывает функцию миниатюр. Как я могу это сделать?

заранее спасибо

Лучшим вариантом будет внедрение Gearman. Это система очереди заданий, в которой вы можете реализовать либо синхронные асинхронные задания. http://gearman.org/

Лучшее решение, для которого я обычно использую: создавать миниатюры динамически при необходимости, а не при загрузке.

Вы создаете скрипт, который генерирует миниатюры «на лету», и все ваши теги изображений указывают на этот скрипт:

<img src="/thumbnail.php?image=foobar.jpg&size=150" /> 

Это задерживает создание миниатюр до тех пор, пока оно не понадобится и работает «асинхронно». С помощью какой-либо переписывающей магии .htaccess вы даже можете сделать ее похожей на обычный файл изображения и кеш-изображения таким образом, что сервер Apache будет обслуживать их в следующий раз, не вызывая скрипт.


Чтобы быть более подробным, я использую это для изображений профиля пользователя:

Теги изображения:

 <img src="/img/users/123456/50.jpg" /> 

.htaccess:

 <IfModule mod_rewrite.c> RewriteEngine On # Rewrites requests for user images to match directory structure. # Eg: URL /img/users/123456/50.jpg -> /img/users/123/123456/50.jpg # Intermediate directory level is introduced to avoid cramming too many directories into the same directory. RewriteRule ^img/users/(\d{1,3})(\d*)/(\d+\.\D+)$ img/users/$1/$1$2/$3 [nocase,last] RewriteCond %{REQUEST_FILENAME} !-d RewriteCond %{REQUEST_FILENAME} !-f RewriteRule ^(.*)$ index.php?url=$1 [QSA,L] </IfModule> 

Это в первую очередь переписывает запросы изображений в более глубокую структуру каталогов. Если изображение существует, Apache будет обслуживать его как обычно. Если это не так, мое обычное приложение вызывается. В приложении URL-адрес route /img/users/... URL-адреса модуля, который заканчивается двумя данными: идентификатор пользователя 123456 и запрошенный размер 50 . Затем он генерирует миниатюру примерно в соответствии с этой логикой:

  1. Найти изображение профиля для пользователя 123456
  2. Создание эскиза в запрошенном размере
  3. Пишите thumbnail /img/users/123/123456/50.jpg , где он будет подхвачен Apache в следующий раз
  4. Выходное изображение

У вас может быть cronjob, который выполняет скрипт thumbnailing. Вы можете добавить изображение, которое должно быть изменено в какой-то очереди (возможно, в базе данных mysql), и скрипт миниатюр запускается каждую минуту, чтобы проверить, есть ли что-то в очереди, а затем начинает изменять размер.

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

 header("Connection: close"); @ob_end_clean(); ignore_user_abort(); ob_start(); //generate and print server response here echo "everything OK"; $size = ob_get_length(); header("Content-Length: ".$size); ob_end_flush(); flush(); //whatever you do here has no influence on the page loading time, as the client has already closed its connection. generateThumbnail(); 

В одной системе я видел независимый фоновый процесс, делающий миниатюры:

  • форма обрабатывается нормально, не генерируя ни одного миниатюры вообще
  • образ получает уникальное имя и копируется в специальную папку.
  • запись в базе данных создается в таблице thumbnails , связывая исходное изображение и новое уникальное имя, помеченное как «миниатюра»,
  • скрипт обработки формы перестает заботиться и продолжает все, что ему нужно.

Существует независимый фоновый процесс (и его сторожевой таймер), который постоянно наблюдает за этой специальной папкой (большинство ОС имеют различные инструменты, которые уведомляют вас о смене содержимого папки); если он найдет изображение там, он будет:

  • сделать эскиз (мы использовали CLI ImageMagick для этого)
  • сохранить его в другом месте
  • обновите базу данных, установите статус изображения как «thumbnailed OK» (или «failed», если он не сможет сделать это)

Когда вам понадобится миниатюра, проверьте таблицу thumbnails – если изображение не «thumbnailed OK», покажите местозаполнитель, иначе получите правильное имя эскиза и покажите это.

Это сработало – большинство эскизов были созданы за несколько секунд, не замедляя скрипты, ориентированные на пользователя. Обратите внимание, что вам нужно как-то запустить фоновый скрипт – в этом случае в cron был сторожевой таймер, который перезапустил скрипт миниатюр, если он умер.


@yankee объекты, что некоторые элементы необычны:

  • нет необходимости, чтобы процесс thumbnailer запускался как фоновый скрипт – если вы можете прожить минуту ожидания, прежде чем вы получите миниатюры, вы можете запустить его как скрипт cron, полностью избавившись от сторожевого пса.
  • ImageMagick был выбран над GD по определенным причинам производительности; thumbnailer может использовать любой доступный метод.

Изменить: я проверил сайт, и есть еще один механизм – этот не нужен и добавляет немного нагрузки, но выглядит круто, особенно если вы не ожидаете, что полная загрузка страницы очень часто (например, на сайтах, управляемых AJAX) :

  • где class="nothumb" not-failed-but-no-thumbnail», миниатюра отображается в img с class="nothumb"
  • функция JS проверяет изображения с этим классом
  • если они обнаружены, он будет периодически проверять, имеется ли эскиз
  • статические заполнители заменяются «загрузкой» заполнителей
  • если он найден, он заменит местозаполнитель миниатюрой

Это загружает миниатюры, как только они будут готовы, за счет некоторых ресурсов. Для непрерывного фонового процесса это действительно не нужно; но если вы хотите, чтобы пользователи увидели миниатюры, когда они станут доступны, а не на следующей странице, это полезное дополнение.

Предложения:

  1. Закройте соединение до завершения сценария, как описано здесь: http://www.php.net/manual/en/features.connection-handling.php#71172

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

  3. Начните еще один процесс, который создает для вас миниатюры. Если у вас есть соответствующие права на сервере, используйте для этого систему (). Если у вас их нет, создайте еще один php-скрипт на своем сервере, который вы вызываете с помощью URL и сокетов. Затем вы можете завершить соединение рано и сохранить скрипт для генерации эскизов. Используйте ignore_user_abort (), чтобы остановить генерацию эскизов, чтобы прервать, как только вы прекратите соединение tcp, открытое с вашим сокетом.

На самом деле существует путь. Если вы отправляете обратно заголовки ответов и контент ответа, вы можете фактически поддерживать поток сервера с вашей обработкой, не оставляя клиента. Таким образом, вы можете отправить заголовки обратно с типом контента и т. Д. Или перенаправить заголовок и т. Д. Браузер прочитает ответ. Но это не означает, что поток сервера останавливается.

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

образец из предоставленной ссылки:

 <?php ob_end_clean(); header("Connection: close\r\n"); header("Content-Encoding: none\r\n"); ignore_user_abort(true); // optional ob_start(); echo ('Text user will see'); $size = ob_get_length(); header("Content-Length: $size"); ob_end_flush(); // Strange behaviour, will not work flush(); // Unless both are called ! ob_end_clean(); //do processing here sleep(5); echo('Text user will never see'); //do some processing ?>