Laravel зависает при запуске команды через exec, не отправляя ее в фоновый режим - PullRequest
0 голосов
/ 14 октября 2018

У меня странная проблема, с которой я застрял пару дней назад.Я пытаюсь сгенерировать PDF в приложении Laravel, используя Chrome без головы, с помощью этой команды
google-chrome --headless --disable-gpu --print-to-pdf=outputfile.pdf http://localurl/pdf-html

Команда в основном открывает Chrome в режиме без головы, перемещается по заданному URL и печатает его как сохранение PDFфайл в указанном месте.Эта команда отлично работает при запуске в оболочке моей системы (я использую Ubuntu 18.04).Теперь моя проблема возникает при попытке запустить ту же команду с контроллера Laravel, я пробовал exec, shell_exec, system и passthru, и все они вызывают у меня одну и ту же проблему.Если я запускаю команду без перенаправления вывода и запуска процесса в фоновом режиме, добавляя >> tmpfile 2>&1 & в конец команды, запрос зависает.Выполнение команды в фоновом режиме обычно не было бы проблемой, за исключением того, что мне нужно завершить команду, чтобы отправить файл обратно клиенту для загрузки.Запустив его в фоновом режиме, он в основном выполняет его асинхронно, и у меня нет никакого способа узнать, когда процесс завершается (или ждать, пока он не закончится), чтобы затем отправить файл в качестве загрузки для ответа.

I 'пробовал другие альтернативы безрезультатно.Я попытался использовать Process Symfony , который поставляется в комплекте с Laravel, но также не работает.Я попытался использовать puppeteer и вместо запуска команды google-chrome использовать скрипт node.js с кодом из документации puppeteer (который, кстати, также работает при запуске непосредственно в моей системной оболочке), нопри запуске из Laravel выдает исключение ошибки тайм-аута навигации.

Наконец, я создал простой php-файл со следующим кодом:

<?php

$chromeBinary = 'google-chrome';
$pdfRenderUrl = "http://localhost:8000/pdf-html";
$fileName = 'invoice.pdf';
$outputDirectory = "/path/to/my/file/" . $fileName;

$command = sprintf(
    '%s --headless --disable-gpu --print-to-pdf=%s %s',
    escapeshellarg($chromeBinary),
    escapeshellarg($outputDirectory),
    escapeshellarg($pdfRenderUrl)
);
exec( $command  );
echo ( file_exists("/path/to/my/file/" . $fileName) ? 'TRUE' : 'FALSE');

?>

И код прекрасно работает при запуске из оболочки, как php thefile.php печать ИСТИНА, означающая, что команда в exec была запущена и после ее завершения файл существует;и это именно тот код, который я использую в Laravel, за исключением того, что он работает только, как упомянуто выше, когда я отправляю процесс в фоновый режим.Кто-нибудь может подсказать мне здесь, пожалуйста?Спасибо

РЕДАКТИРОВАТЬ: @namoshek спасибо за быстрый ответ и извините, если я не прояснил себя.Проблема не в долгом времени ожидания, возможно, я мог бы жить с этим.Проблема в том, что exec никогда не завершается, и мне в конечном итоге приходится принудительно завершать процесс (ни exec, ни какая-либо другая альтернатива, они все навсегда замораживают запрос, за исключением Process, который завершается неудачей из-за исключения TimeoutException).Я использую почтальон для запроса конечной точки.Интерфейс - это приложение Angular, означающее, что запрос на загрузку счета будет в конечном итоге выполняться асинхронно.Кроме того, сама задача не является длительной задачей, поскольку, по сути, она заканчивается довольно быстро.Использование стратегии опроса или системы уведомлений, на мой взгляд, не представляется жизнеспособным решением.Представьте себе приложение с кнопкой загрузки, чтобы загрузить простой документ, и вам нужно нажать на кнопку и подождать, пока приложение уведомит вас по электронной почте (или другим способом) о том, что документ готов.Я мог бы понять это, если бы это был более сложный процесс, но загрузка документа кажется чем-то тривиальным.Но что меня смущает, так это то, что запуск задачи из сценария php работает так, как я хочу (синхронно), и я не могу воспроизвести поведение на контроллере laravel

EDIT: I 'Мы также пытались использовать BrowserShot , что, кстати, также не удается.Browsershot предоставляет способ взаимодействия, за кулисами с кукловодом, используя Process, и создание файла PDF.И хотя это внешняя программа, мне все еще кажется, что поведение, которое я получаю, не является нормальным, я должен быть в состоянии получить загрузку, даже если для выполнения запроса потребовалось 10 секунд, поскольку внешняя программа выполнялась синхронно.Но в моем случае это происходит из-за ошибки тайм-аута

РЕДАКТИРОВАТЬ: Итак, через некоторое время я столкнулся с очевидной причиной зависания сервера.Проблема в том, что я использовал сервер разработки Artisan.Изначально это не казалось мне проблемой, но кажется, что ремесленник не может справиться с такой нагрузкой.В функции, которую я реализую, я выполняю запрос к определенной конечной точке, назовем ее конечной точкой 1, чтобы сгенерировать pdf, код этой конечной точки запускает внешнюю команду, а при синхронном выполнении это означает код в конечной точке 1.ожидает завершения внешней команды.Внешняя команда, в свою очередь, должна перейти к конечной точке 2 на том же сервере, конечная точка 2 содержит представление html с содержимым, которое должно быть помещено в pdf, поскольку сервер все еще ожидает в конечной точке 1 возврата внешней команды, а затем в конечную точку.2 не отвечает, по-видимому, создает цикл, который сервер разработки ремесленника не может обработать.Проблема в том, что я сделал быстрый поиск и не нашел ничего, что указывало бы на неэффективность сервера разработки artisan.Я переместил среду в Apache только для проверки своей теории, и она сработала, хотя следует отметить, что выполнение запроса занимает очень много времени (около 10-20 секунд).Пока это кажется единственным разумным объяснением того, почему эта проблема происходила.Если кто-нибудь знает, как я могу улучшить производительность по этому запросу, или кто-то может дать лучшее объяснение первоначальной проблеме, я был бы признателен

1 Ответ

0 голосов
/ 14 октября 2018

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

Итак, в основном осталось только два варианта: жить с длительным временем ожидания (если вы хотите выполнить задачу синхронно) или выполнять запрос / задачу асинхронно.Последнее может быть достигнуто двумя способами:

  1. Сделайте фактический HTTP-запрос выполненным в фоновом режиме (используя ajax) и используйте индикатор загрузки, чтобы сохранить терпение ваших пользователей.Таким образом, вы по-прежнему можете запускать процесс синхронно, но я бы не рекомендовал делать это, поскольку вам пришлось бы использовать большое время ожидания для запроса ajax, а в некоторых ситуациях запросы могли бы все еще оставаться в таймауте (в зависимости от рабочей нагрузки вашего сервера).
  2. Используйте силу Laravel и используйте работников очереди для выполнения расчетов в фоновом режиме.Когда создание снимка завершено, вы можете использовать один из следующих трех параметров, чтобы вернуть результат:
    • Используйте опрос на стороне клиента, чтобы проверить, доступен ли результат.
    • Отправьтерезультат или ссылка на результат по почте или что-то похожее на пользователя.
    • Используйте систему уведомлений, чтобы уведомить пользователя о завершенном процессе и каким-то образом вернуть результат (то есть получить его или отправить как частьуведомления - есть много доступных вариантов).Встроенная система уведомлений, которая в точности соответствует описанному мной, Laravel Echo .После получения уведомления о завершении процесса вы можете получить результат с сервера.

В настоящее время стандартом для веб-приложений и взаимодействия с пользователем является параметр2 с системой уведомлений (3-й пункт).

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...