在Laravel的artisan queue:listen中捕获ProcessTimedOutException异常

21

我们有一堆要监视并使用Laravel的php artisan queue:listen处理的SQS作业。

周期性地,SQS会有几分钟的中断并超时。当这种情况发生时,queue:listen会中止,并显示以下消息:

[Symfony\Component\Process\Exception\ProcessTimedOutException]                                                                                             
  The process "php artisan queue:work
    --queue="https://sqs.us-west-2.amazonaws.com/*******/queue"
    --delay=0 --memory=128 --sleep=3 --tries=0 -env=production"
  exceeded the timeout of 60 seconds.

我已经尝试在app/start/global.php或者app/start/artisan.php中处理异常:

App::error(function(Symfony\Component\Process\Exception\ProcessTimedOutException $exception) {
    // do nothing
});

不幸的是,异常仍然发生,我的queue:listen仍然停止运行。

我该如何捕获这个异常并忽略它以进行重试?


你的“什么都不做”的异常处理程序必须返回一些内容,否则默认的处理程序将被调用。请参阅Illuminate\Exception\Handler::handleException() - eggyal
然而,即使您返回某些内容并因此避免了默认处理程序,监听器仍将在引发异常后中止。我认为这种行为无法改变。为什么不增加超时时间或将其设置为“null”以实现无限制? - eggyal
1
@eggyal 我敢肯定我们以一种允许继续执行的方式处理了一些 App::error 异常。我得检查一下。增加超时时间是不可行的 - 如果 SQS 永远不响应,我不能让这个东西永远运行。 - ceejayoz
可能你是对的。我对Symfony和Laravel都不熟悉 - 这只是我从阅读源代码得出的结论,虽然我也很乐意承认我可能误解了异常处理过程。 - eggyal
@ceejayoz 你不能这样做。你可以让错误处理程序继续到下一个,但最终由于未捕获的异常而调用了App::error处理程序,这将导致您的应用程序终止。 - Andreas
1
想知道你现在解决了这个问题没有。我也遇到了beanstalkd监听器超时的相同问题。 - Luceos
7个回答

1
如果有帮助的话,我使用SupervisorD来运行cron,然后每秒钟触发一个queue:work,这样就不会出现SQS的任何问题。

0
<?php

namespace App;

use Illuminate\Bus\Queueable;
use Illuminate\Contracts\Queue\ShouldQueue;
use Illuminate\Foundation\Bus\Dispatchable;
use Illuminate\Queue\InteractsWithQueue;
use Illuminate\Queue\SerializesModels;


abstract class Job implements ShouldQueue, JobStatusInterface
{
    use Dispatchable, InteractsWithQueue, Queueable, SerializesModels;

    public $tries = 1; // try only once

    public function handle()
    {

    }
}

使用这个抽象类,并将其分派给扩展类,它可以解决你的问题。
<?php

namespace App;

use App\Job;
use Log;
use Exception;
/**
 * Class CheckStockJob
 */
 class CheckStockJob extends Job
 {
     public $tries = 100; // how many you need to retry
     public function handle()
     {
        try {
          // do something here
        }
        catch(Exception $e){
          Log::error($e);
          throw $e; // rethrow to make job fail
        }
     }
 }

在你的代码中的某个地方

$t = new CheckStockJob();
dispatch($t);

0

这不是一个答案,而更像是一个解决方法:

每当我需要确保进程即使在出现错误的情况下也能运行时,我都会使用 npms forever。这个方便的工具将在进程崩溃时立即重新启动它。

安装非常简单明了:

sudo apt-get install nodejs
sudo apt-get install npm
sudo ln -s /usr/bin/nodejs /usr/bin/node
sudo npm -g install forever

使用此命令启动进程:forever start -c php artisan queue:listen
如果您需要停止它:forever stopall


0

你要么在异常处理程序中重新运行了 "php artisan queue:work ..." 队列作业,

要么将 /vendor/symfony/process/Symfony/Component/Process/Process::setTimeout() 的值增加到更高的数值。

或者两者都有可能。


0

该问题似乎与队列作业运行时间有关。据我所知,默认运行时间为30秒,您应该增加此值。您可以通过两种方式来增加:

php artisan queue:listen --timeout=1200

或者您可以为特定作业设置$timeout

 /**
 * The number of seconds the job can run before timing out.
 *
 * @var int
 */
public $timeout = 1200;

希望这能有所帮助


最终,这只是问题的权宜之计 - 它会减少崩溃的可能性,但如果它达到1200秒queue:listen仍然会死掉。我最终使用的解决方法是Laravel Horizon及其监管设置。 - ceejayoz
你尝试过使用 php artisan queue:work --timeout=1200 吗?在 1200 秒后出现了错误吗? - webdevtr
再次延长超时时间只是延长了进程抛出“ProcessTimedOutException”的时间。需要1,201秒的作业将会出错。(我也不希望大多数排队的作业被允许运行那么长时间。这里的目标是让作业在合理的时间内超时,并且如果它们被卡住了,不会崩溃queue:listen进程。) - ceejayoz
我看到了,我认为解决方案是,你应该根据特定任务需要完成的时间定义 public $timeout = xxxx; - webdevtr
仍未理解重点。超时会导致 queue:listen 进程崩溃。我希望它可以超时 并且 保持 queue:listen 进程的活动状态,以继续处理其他作业,而不是因超时而崩溃。 - ceejayoz

0

超时有时会发生。Symfony进程类具有自己的超时设置,这可能导致特定的超时(我认为)。我不确定是否可以在不编辑与工作人员相关的核心Laravel代码的情况下捕获此问题,这不是一个好主意。

我强烈建议按照Laravel文档中所示使用supervisor:

https://laravel.com/docs/5.1/queues#supervisor-configuration

这将自动重新启动您的侦听器,即使它失败了。

-2

尝试通过执行以下命令来调查可用参数:

php artisan queue:listen --help

您可以传递超时值(以秒为单位)、尝试次数等参数。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接