【问题标题】:Laravel 5.1 failed queued jobs fails on failed() method, prevents queue failure event handler from being calledLaravel 5.1 失败的排队作业在 failed() 方法上失败,防止调用队列失败事件处理程序
【发布时间】:2015-09-27 03:00:20
【问题描述】:

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

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

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

仅供参考,我已经按照 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() 函数永远不会执行,它会阻止调用下一个函数 raiseFailedJobEvent()。就好像在调用 failed() 时脚本会静默停止。现在,如果我颠倒这些行的顺序,我可以让 raiseFailedJobEvent() 触发,如果我在 EventServiceProvider.php 或 AppServiceProvider.php 中注册一个队列事件处理程序,我可以验证它被触发并且我可以成功处理该事件。不幸的是,在 raiseFailedJobEvent() 之前设置 failed() 可以防止此事件发生。

更新:

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

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

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

【问题讨论】:

  • 我遇到了类似的问题。即使 laravel 检测到作业失败并将其添加到 failed_jobs 表中,我也无法在 5.0 中对我的命令触发 failed() 方法。我很想打开这个错误,但肯定不能从根本上破坏它吗?它在文档中,所以一定有人用过。
  • 我认为它从根本上被破坏了。我现在没有时间,但是是的,我说把它发布到他们的 github 上。
  • 谢谢肖恩。我很高兴 Graham 回复了你,虽然我不确定他说“你需要将你的处理程序作为一个单例绑定到容器上”是什么意思。我想我需要看更多的 Laracast ;)

标签: laravel queue laravel-5 jobs


【解决方案1】:

从我在https://github.com/laravel/framework/issues/9799 与 Graham 的对话中尝试一下

最后,我能找到的最优雅的解决方案是在作业类本身上触发 failed() 方法是添加到下面的 EventServiceProvider.php 的 boot() 方法中。捕获触发的完整失败事件,然后挖掘命令/作业并将其反序列化以调用 failed() 方法。

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

【讨论】:

【解决方案2】:

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

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

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

【讨论】:

  • 我在写入作业队列表或作业失败表时没有问题,因为事件没有被触发,所以我无法在失败时记录或提醒我们的开发人员。
  • 这应该记录失败。你试过了吗?
  • 确实如此。但我实际上需要提醒我们的团队。我输入的日志功能只是为了验证失败事件是否有效。我希望我能弄清楚为什么这对 Laravel 5.1 不起作用
  • 这是一种很老套的方法,但您可以跟踪日志以查找故障并以这种方式发出警报。
猜你喜欢
  • 2014-11-19
  • 2019-07-10
  • 2018-08-16
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2012-07-11
  • 2020-11-04
相关资源
最近更新 更多