我们正在运行带有主管/SQS的Laravel 4,并且我们使用10个工作进程运行30 +不同的任务。一切都进展顺利,但是某些任务似乎已经开始超时。我们得到这样的异常:
[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
的工作原理做了更多的研究。一些参考资料:
- 亮起/队列/控制台/侦听 亮起/
- 队列/侦听 器
- Symfony/Component/Process
基本上,当你调用php artisan queue:listen
时,会创建一个子进程(使用Symfony/Component/Process),它本质上运行命令php artisan queue:work
。该子进程从队列中获取下一个作业,运行它,在完成时报告,然后侦听器生成另一个子进程来处理下一个作业。
因此,如果其中一个子进程花费的时间超过建立的超时限制,则父侦听器将引发异常,但是,父实例没有关于它创建的子进程的数据。除了一点例外!看起来父侦听器确实处理子进程的输出。在我看来,父进程只不过是将子进程(worker)输出渲染到控制台。但是,也许有一种方法可以捕获此输出,以便在抛出异常时,我们可以记录输出,从而了解发生超时时哪个任务正在运行!
我还注意到,当使用 supervisord 时,我们能够指定一个记录所有工作线程输出的stdout_logfile
。现在,我们正在为所有 10 个受监督的"程序"使用单个日志文件。我们可以将其更改为让每个"程序"使用自己的日志文件,然后当在父侦听器上抛出超时异常时,我们可以让它获取该日志文件的最后 10 行。这也将为我们提供有关在超时期间正在运行哪些任务的信息。但是,我不确定如何"通知"父侦听器它正在运行哪个主管程序,以便它知道要查看哪个日志文件!
查看异常类 ( Symfony'Component'Process'Exception'ProcessTimedOutException
) 我找到了返回 Symfony'Component'Process'Process
实例的方法getProcess()
。在那里你得到了getOutput()
.该方法按照其名称所述进行操作。
正如您在注释中建议的那样,您可以通过回显每个任务中的类名和参数,然后使用生成的输出来确定有问题的任务来使用它。正如你所说,它不是很优雅,但我想不出更好的方法(除了可能修补Illuminate'Queue'Listener
类......
这是一个如何做到这一点的示例(尽管未经测试)
我选择了这种格式作为输出:
ClassName:ParametersAsJson;ClassName:ParametersAsJson;
所以在基本任务中,我会这样做:
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的网站上找到。
总结一下:
- Laravel可以将失败的作业移动到
failed_jobs
表中以供以后查看 - 您可以通过
Queue::failing
注册异常处理程序,这将接收详细的作业信息以便立即处理
但是,在 Laravel 中是否将超时视为失败是值得怀疑的,因此这需要测试,因为我没有队列的实践经验。
如果您使用 Laravel 4.0,也许值得评估至少升级到 4.1 的升级,而不是编写复杂的代码,一旦您真的必须升级,这些代码就会变得多余(您将在某个时候升级,对吧? :))。升级路径似乎非常简单。
虽然这并不能直接回答您对 Laravel 4.0 的问题,但这是您和任何未来读者都可以考虑的问题。