Как долгий PHP скрипт отправить в фон и одновременно вернуть пользователю сообщение об ожидании его исполнения?

Исходники:

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

Все работает и исполняется прекрасно, но, естественно, из-за долгого времени исполнения выводятся ошибки об истечении времени, т.н. timeout'ы.

На сервере: nginx и PHP-FPM.
Используется Symfony.

PHP скрипт:

use Symfony\Component\Process\Process;

$process = Process::fromShellCommandline("sudo /usr/bin/ansible-playbook -i {$inventoryFile}'");
$process->setTimeout(3600);

set_time_limit(0);

$process->run();

sleep(1);

return 'ok_response';


Предпринято следующее:

В /etc/php/cli/php.ini:
max_execution_time = 0

В /etc/php/fpm/php.ini:
max_execution_time = 300

В /etc/nginx/nginx.conf:
keepalive_timeout  30;
keepalive_requests 1000;
reset_timedout_connection on;
client_header_timeout 30s;
client_body_timeout 30s;
send_timeout 15s;


Для PHP-FPM:
fastcgi_read_timeout 300s;
fastcgi_send_timeout 300s;


Таким образом, думаю, что все timeout'ы подняты.

Также в пользовательской форме создан AJAX запрос. который хоть и не рушит саму страницу 504 ошибкой gateway time-out, но, тем не менее, возвращает сообщение сайта (в виде "красного" уведомления) об ошибке - сам скрипт выполняется успешно в фоне.

Вопрос простой:
Как отправить вышеприведенный кусок PHP скрипта на исполнение в фон, а пользователю вернуть что-то типа: "Ждите, скоро все будет"?
(Соответственно, по истечении исполнения PHP скрипта ему пришлют все нужные уведомления, что происходит и сейчас.)
  • Вопрос задан
  • 794 просмотра
Пригласить эксперта
Ответы на вопрос 4
@KingstonKMS
Сделать консольный воркер, который будет выполнять задачи в порядке очереди.
1. Принимать задачи или брать из базы какой то, типа редис.
2. Выполнять её.
3. Сохранять уведомления в базу.
4. Отправлять уведомление пользователю.
Это позволит, обрабатывать задачи консольно, в очереди, не создавая запуск нескольких экземпляров скриптов, например, что бывает через крон, уведомить юзера при завершении, сайт не нужно будет держать открытым и ждать выполнения.
Ответ написан
daager
@daager
Не самый красивый, но самый простой способ т.к. у вас NGinx - fastcgi_finish_request
Ответ написан
@Kirill-Gorelov
С ума с IT
TЕсть еще способ, не самый технологичный, но самый простой.
Записывать состояние в БД.
И обновлять его если оно изменилось. А пользователю просто показывать статус.

Быстро, просто, надежно, но не круто((
Ответ написан
@AndryG
Я в коде для таких задач имею скриптик лет 10 назад написанный:
public static function execAsDeamon($script,$params = null){
    $tmp = '';
    foreach((array)$params as $k=>$v){
      if(empty($k)){
        continue;
      }
      $tmp .= ' -'.$k.'='.$v;
    }
    $tmp = g::options('core.php-cli-starter', 'php').' '.$script.' '.srv::$HOST_TYPE.$tmp.' > /dev/null &';
    exec($tmp);
  }


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

Само собой, в логике приложения есть предохранитель от многократного запуска демона.
Ответ написан
Комментировать
Ваш ответ на вопрос

Войдите, чтобы написать ответ

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