Laravel queue:listen 超时时获取当前正在运行的任务
Get the current task being run when Laravel queue:listen times out
我们正在使用 supervisord / SQS 运行 Laravel 4,我们有 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 Listener 会抛出异常,但是,父实例没有关于它创建的子进程的数据。有轻微的例外!看起来父监听器确实处理子进程的输出。在我看来,父进程只不过是将子进程(工作者)的输出呈现到控制台。但是,也许有一种方法可以捕获此输出,以便在抛出异常时记录输出,从而了解发生超时时哪个任务正在运行!
我还注意到,在使用 supervisord 时,我们可以指定一个 stdout_logfile
来记录所有工作人员的输出。现在我们正在为我们所有的 10 个 supervisord "programs" 使用一个日志文件。我们可以将其更改为让每个 "program" 使用它自己的日志文件,然后也许当父监听器抛出超时异常时,我们可以让它获取该日志文件的最后 10 行。这也将为我们提供有关超时期间正在运行哪些任务的信息。但是,我不确定如何 "inform" 父 Listener 正在运行哪个 supervisord 程序,以便它知道要查看哪个日志文件!
查看异常 class (Symfony\Component\Process\Exception\ProcessTimedOutException
) 我发现方法 getProcess()
其中 returns 是 Symfony\Component\Process\Process
的一个实例。那里有 getOutput()
。该方法如其名称所述。
正如您在评论中所建议的那样,您可以通过在每个任务中回显 class 名称和参数来使用它,然后使用生成的输出来确定有问题的任务。正如你所说,它不是很优雅,但我想不出更好的方法(除了可能修补 Illuminate\Queue\Listener
class...)
这里有一个你可以如何做到的例子(虽然未经测试)
我为输出选择了这种格式:
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's website.
上提供了详细而清晰的文档
总结一下:
- Laravel 可以将失败的作业移至
failed_jobs
table 以供以后查看
- 您可以通过
Queue::failing
注册一个异常处理程序,它将收到详细的作业信息以便立即处理
然而,在Laravel中超时是否被视为失败是值得怀疑的,所以这需要测试,因为我没有队列的实践经验。
如果您使用 Laravel 4.0,也许值得评估至少升级到 4.1,而不是编写复杂的代码,一旦您真的必须升级,这些代码就会变得多余(您 将 在某个时候升级,对吧?:))。 Upgrade path 看起来很简单。
虽然这不能直接回答您 Laravel 4.0 的问题,但您和任何未来 reader 都可以考虑。
我们正在使用 supervisord / SQS 运行 Laravel 4,我们有 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 Listener 会抛出异常,但是,父实例没有关于它创建的子进程的数据。有轻微的例外!看起来父监听器确实处理子进程的输出。在我看来,父进程只不过是将子进程(工作者)的输出呈现到控制台。但是,也许有一种方法可以捕获此输出,以便在抛出异常时记录输出,从而了解发生超时时哪个任务正在运行!
我还注意到,在使用 supervisord 时,我们可以指定一个 stdout_logfile
来记录所有工作人员的输出。现在我们正在为我们所有的 10 个 supervisord "programs" 使用一个日志文件。我们可以将其更改为让每个 "program" 使用它自己的日志文件,然后也许当父监听器抛出超时异常时,我们可以让它获取该日志文件的最后 10 行。这也将为我们提供有关超时期间正在运行哪些任务的信息。但是,我不确定如何 "inform" 父 Listener 正在运行哪个 supervisord 程序,以便它知道要查看哪个日志文件!
查看异常 class (Symfony\Component\Process\Exception\ProcessTimedOutException
) 我发现方法 getProcess()
其中 returns 是 Symfony\Component\Process\Process
的一个实例。那里有 getOutput()
。该方法如其名称所述。
正如您在评论中所建议的那样,您可以通过在每个任务中回显 class 名称和参数来使用它,然后使用生成的输出来确定有问题的任务。正如你所说,它不是很优雅,但我想不出更好的方法(除了可能修补 Illuminate\Queue\Listener
class...)
这里有一个你可以如何做到的例子(虽然未经测试)
我为输出选择了这种格式:
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's website.
上提供了详细而清晰的文档总结一下:
- Laravel 可以将失败的作业移至
failed_jobs
table 以供以后查看 - 您可以通过
Queue::failing
注册一个异常处理程序,它将收到详细的作业信息以便立即处理
然而,在Laravel中超时是否被视为失败是值得怀疑的,所以这需要测试,因为我没有队列的实践经验。
如果您使用 Laravel 4.0,也许值得评估至少升级到 4.1,而不是编写复杂的代码,一旦您真的必须升级,这些代码就会变得多余(您 将 在某个时候升级,对吧?:))。 Upgrade path 看起来很简单。
虽然这不能直接回答您 Laravel 4.0 的问题,但您和任何未来 reader 都可以考虑。