Мы запускаем Laravel 4 с supervisord / SQS, и у нас более 30 различных задач, выполняемых с использованием 10 рабочих процессов. Все идет очень хорошо, однако кажется, что определенные задачи начали истекать. Мы получаем исключение как это:
[Symfony\Component\Process\Exception\ProcessTimedOutException]
The process ""/usr/bin/php5" artisan queue:work --queue="https://sqs.us-east- 1.amazonaws.com/xxxx" --delay=0 --memory=128 --sleep=3 --tries=0 --env=development" exceeded the timeout of 180 seconds.
Я могу поймать это исключение, используя это:
App::error(function(Symfony\Component\Process\Exception\ProcessTimedOutException $exception) {
/// caught!
});
Однако я не могу определить, КАКОЕ задание выполняется (когда истекает время ожидания), и даже лучше, если я смогу получить доступ к данным, которые были переданы задаче.
Я попытался записать трассировку стека объектов исключения:
$exception->getTraceAsString()
Тем не менее, это не дает мне достаточно подробностей о задаче, которая была вызвана.
ОБНОВИТЬ
Я сделал больше исследований о том, как php artisan queue:listen
работает. Некоторые ссылки:
В основном, когда вы звоните php artisan queue:listen
создается подпроцесс (с использованием Symfony / Component / Process), который по существу запускает команду php artisan queue:work
, Этот подпроцесс выбирает следующее задание из очереди, запускает его, сообщает о завершении, а затем прослушиватель запускает другой подпроцесс для обработки следующего задания.
Таким образом, если один из подпроцессов занимает больше времени, чем установленный предел времени ожидания, прослушиватель PARENT выдает исключение, однако родительский экземпляр не имеет данных о подпроцессе, который он создал. С легким исключением! Похоже, что родительский слушатель обрабатывает вывод подпроцесса. Мне кажется, что родитель не делает ничего, кроме как выводит вывод подпроцесса (рабочий) на консоль. Однако, возможно, есть способ перехватить эти выходные данные, чтобы при возникновении исключения мы могли регистрировать выходные данные и, следовательно, иметь представление о том, какая задача выполнялась, когда истекло время ожидания!
Я также заметил, что при использовании супервизора, мы можем указать stdout_logfile
который регистрирует весь вывод работника. Прямо сейчас мы используем один файл журнала для всех наших 10 супервизорных «программ». Мы могли бы изменить это так, чтобы каждая «программа» использовала свой собственный файл журнала, а затем, возможно, когда исключение тайм-аута было сгенерировано для родительского прослушивателя, мы могли бы заставить его захватить последние 10 строк этого файла журнала. Это также даст нам информацию о том, какие задачи выполняются в течение тайм-аута. Однако я не уверен, как «сообщить» родительскому слушателю, какая программа супервизора работает, чтобы он знал, какой файл журнала посмотреть!
Глядя на класс исключения (Symfony\Component\Process\Exception\ProcessTimedOutException
) Я нашел метод getProcess()
который возвращает экземпляр Symfony\Component\Process\Process
, Там у вас есть getOutput()
, Метод делает то, что говорит его имя.
Как вы предложили в комментариях, вы можете использовать это, отображая имя класса и параметры в каждой задаче, а затем используя сгенерированный вывод для определения проблемной задачи. Как вы сказали, это не очень элегантно, но я не могу придумать лучшего способа (кроме, может быть, возиться с Illuminate\Queue\Listener
учебный класс…)
Вот пример, как вы могли бы сделать это (хотя и не проверено)
Я выбрал этот формат для вывода:
ClassName:ParametersAsJson;ClassName:ParametersAsJson;
Так что в BaseTask я бы сделал это:
abstract class BaseTask {
public function fire(){
echo get_class($this) . ':' . json_encode(func_get_args()) . ';';
}
}
К сожалению, это означает, что в каждом задании вам придется звонить parent::fire
class Task extends BaseTask {
public function fire($param1, $param2){
parent::fire($param1, $param2);
// do stuff
}
}
И, наконец, обработчик исключений:
App::error(function(Symfony\Component\Process\Exception\ProcessTimedOutException $exception) {
$output = $exception->getProcess()->getOutput();
$tasks = explode(';', $output);
array_pop($output); // remove empty task that's here because of the closing ";"$lastTask = end($tasks);
$parts = explode(':', $lastTask);
$className = $parts[0];
$parameters = json_decode($parts[1]);
// write details to log
});
Начиная с Laravel 4.1, существует встроенный механизм для обработки невыполненных заданий, в котором все сведения о задании сохраняются в базе данных или доступны во время исключения (или в обоих случаях). Подробная и понятная документация доступна на Веб-сайт Laravel.
Чтобы подвести итог:
failed_jobs
таблица для последующего просмотраQueue::failing
, который получит подробную информацию о работе для немедленной обработкиОднако сомнительно, что тайм-аут считается ошибкой в Laravel, поэтому это требует тестирования, так как у меня нет практического опыта работы с очередями.
Если вы используете Laravel 4.0, возможно, было бы целесообразно оценить обновление по крайней мере до 4.1, вместо того, чтобы писать сложный код, который станет избыточным, когда вам действительно потребуется обновление (вы будут обновить в какой-то момент, верно? :)). Путь обновления кажется довольно простым.
Хотя это не дает прямого ответа на ваш вопрос о Laravel 4.0, это то, что вы и любой будущий читатель можете рассмотреть.