Laravel 5.1 失败的排队作业在 failed() 方法上失败,阻止调用队列失败事件处理程序

Laravel 5.1 failed queued jobs fails on failed() method, prevents queue failure event handler from being called

我正在 Laravel 5.1 中测试队列功能。我可以让作业在我的数据库 table 中排队,称为作业,我可以让它们成功到达 运行。我还创建了一个名为 failed_jobs 的队列故障 table。为了测试它,在作业 table 中,我操纵有效载荷数据使其失败,然后我 运行 队列工作守护进程像这样,所以它会将作业放在 failed_jobs table 一次失败尝试后:

php artisan queue:work --daemon --tries=1 --queue=myqueue

当作业失败时,它会立即按预期放入 failed_jobs table。

仅供参考,我已经按照 Laravel 5.1 文档推荐的方式进行了设置:

http://laravel.com/docs/5.1/queues#dealing-with-failed-jobs

我已经尝试在 AppServiceProvider 的 boot() 方法中注册我的队列失败事件,如文档中所述:

Queue::failing(function ($connection, $job, $data) {
            Log::error('Job failed!');
        });

我也在实际的作业脚本中尝试过 failed() 方法,如下所示:

/**
         * Handle a job failure.
         *
         * @return void
         */
        public function failed()
        {
            Log::error('failed!');
        }

无论哪种方式,当排队的作业失败时都不会触发这些事件。除了我故意发生的异常堆栈跟踪外,我在日志中什么也看不到。 Laravel 5.1 这里有错误还是我遗漏了什么?

更新:

我做了更多的研究。当队列任务失败时,失败的处理逻辑在vendor/laravel/framework/src/Illuminate/Queue/Worker.php:

protected function logFailedJob($connection, Job $job)
    {
        if ($this->failer) {
            $this->failer->log($connection, $job->getQueue(), $job->getRawBody());

            $job->delete();

            $job->failed();

            $this->raiseFailedJobEvent($connection, $job);
        }

        return ['job' => $job, 'failed' => true];
    }

发生的事情是 failed() 函数永远不会执行,它会阻止调用下一个函数 raisedFailedJobEvent()。就好像脚本在调用 failed() 时静默停止一样。现在,如果我颠倒这些行的顺序,我可以让 raiseFailedJobEvent() 触发,如果我在 EventServiceProvider.php 或 AppServiceProvider.php 中注册一个队列事件处理程序,我可以验证它被触发并且我可以成功处理事件。不幸的是,在 raiseFailedJobEvent() 之前使用 failed() 会阻止此事件的发生。

更新:

问题似乎源于我如何让它失败。如果我故意破坏作业队列 table 中的数据,则永远不会调用 failed() 方法。日志中有堆栈跟踪:

Stack trace:
#0 [internal function]: Illuminate\Foundation\Bootstrap\HandleExceptions->handleError(8, 'unserialize():

如果我真的进入 vendor/laravel/framework/src/Illuminate/Queue/Worker.php 并在每次 运行 时强制它失败(当然是以一种无异常的方式),那么 failure()被调用。显然,问题是我如何知道这个队列在实际故障中的表现?如果损坏的数据库数据导致失败但又阻止调用 failure() ,这就不好了。如果现实世界中存在数据库队列数据的实际损坏怎么办?

如果你像这样启动队列监听器

nohup php artisan queue:listen > storage/logs/queue.log 2>&1 &

然后将自动创建并填充一个队列日志文件。

从我在 https://github.com/laravel/framework/issues/9799

与 Graham 的谈话中尝试这个

最后,我能找到的最优雅的解决方案是让 failed() 方法触发作业 class 本身是将下面的内容添加到 [=17 的 boot() 方法中=].捕获触发的完整失败事件,然后挖掘出 command/job 并将其反序列化以调用 failed() 方法。

Queue::failing(function($connection, $job, $data)
        {
            $command = (unserialize($data['data']['command']));
            $command->failed();
        });